{"id":22213153,"url":"https://github.com/corp-gp/active_dry_deps","last_synced_at":"2025-07-27T12:31:01.397Z","repository":{"id":188813208,"uuid":"678578833","full_name":"corp-gp/active_dry_deps","owner":"corp-gp","description":"Simple IoC for ruby, rails","archived":false,"fork":false,"pushed_at":"2024-11-18T19:21:57.000Z","size":25,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-18T20:31:17.521Z","etag":null,"topics":["dependency-injection","ioc","rails","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/corp-gp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-08-14T22:09:53.000Z","updated_at":"2024-11-18T19:22:01.000Z","dependencies_parsed_at":"2023-08-17T01:39:42.805Z","dependency_job_id":"34635702-20f9-4245-a075-4f1b9c2019d0","html_url":"https://github.com/corp-gp/active_dry_deps","commit_stats":{"total_commits":5,"total_committers":2,"mean_commits":2.5,"dds":0.4,"last_synced_commit":"38e63fd9303bace0588f3b8514bc809bd9acf1f3"},"previous_names":["corp-gp/active_dry_deps"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corp-gp%2Factive_dry_deps","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corp-gp%2Factive_dry_deps/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corp-gp%2Factive_dry_deps/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corp-gp%2Factive_dry_deps/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/corp-gp","download_url":"https://codeload.github.com/corp-gp/active_dry_deps/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227802879,"owners_count":17822113,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency-injection","ioc","rails","ruby"],"created_at":"2024-12-02T21:08:47.004Z","updated_at":"2025-07-27T12:31:01.360Z","avatar_url":"https://github.com/corp-gp.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Installation\n\nInstall the gem and add to the application's Gemfile by executing:\n\n    $ bundle add active_dry_deps\n\n## Dependency Injection\n\nDependency injection helps to break explicit dependencies between objects making\nit much easier to maintain a [single\nresponsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle)\nand reduce [coupling](https://en.wikipedia.org/wiki/Coupling_(computer_programming))\nin our class designs. This leads to more testable code and code that is more\nresilient to change.\n\nFor a deeper background on Dependency Injection consider the\n[Wikipedia](https://en.wikipedia.org/wiki/Dependency_injection) article on the\nsubject.\n\n## Usage\n\n### Basic\nDependencies are injected by listing their names: `Deps['Warehouse::CreateDepartureService.call']`. This notation is familiar to Ruby developers. It helps to find code in the project (compares to abstract container keys), and simplifies the migration from constants in code to defining dependencies\n\n```ruby\nclass CreateOrderService\n  include Deps[\n    'Warehouse::CreateDepartureService.call',\n    'Warehouse::ReserveJob.perform_later',\n    'OrderMailer',\n    'redis',\n    track: 'StatsApi.message',\n  ]\n\n  def call(params)\n    order = Order.create(params)\n\n    ReserveJob(order)\n    track(order.id, order.created_at)\n\n    redis.with do |conn|\n      conn.incr('order_count')\n    end\n\n    OrderMailer().with(user: user).deliver_later\n\n    CreateDepartureService(order.slice(:id, :departure_at))\n  end\n\nend\n```\n\nRspec matcher `deps` allows to isolate dependencies in tests. It simplifies unit testing\n\n```ruby\nRspec.describe CreateOrderService do\n  it 'success create order' do\n    service = described_class.new(user: create(:user), zip_code: 67_345)\n    expect(service).to deps(CreateDepartureService: double(success?: true), ReserveJob: spy, track: spy)\n\n    expect(service.call.success?).to be true\n  end\nend\n\n```\n\n#### Register custom dependency\nYou can define an arbitrary object as a dependency with method `Deps.register`\n\n```ruby\nclass OrderMailer\n  def send_mail = 'email sent'\nend\n\nDeps.register('mailer') { OrderMailer.new }\n\nclass CreateOrderService\n  include Deps['mailer']\n\n  def call\n    mailer.send_mail\n  end\nend\n\nCreateOrderService.new.call # =\u003e email sent\n```\n\n### Import methods\nYou can inject any method from constant as dependency\n\n```ruby\nclass OrderRepository\n  def self.overdue_order_ids = [1, 2, 3]\nend\n\ninclude Deps['OrderRepository.overdue_order_ids']\n\noverdue_order_ids # =\u003e [1, 2, 3]\n```\n\n### Import callable methods\nThere is a special convention for naming some methods. By default, when `call` or `perform_later` methods are imported, the name of the dependency is taken from the name of the constant, not by method name\n\n```ruby\ninclude Deps[\n  'Warehouse::CreateDepartureService.call', # callable\n  'Warehouse::ReserveJob.perform_later', # callable\n  'Warehouse::ReserveJob.perform_now',\n  'Warehouse::ProductActivateQuery',\n]\n\n# use as\nCreateDepartureService() # Warehouse::CreateDepartureService.call\nReserveJob() # Warehouse::ReserveJob.perform_later\nperform_now # Warehouse::ReserveJob.perform_now\nProductActivateQuery().run # Warehouse::ProductActivateQuery.run\n```\n\nRecommends using suffixes (`Service`, `Job`, `Query`) in the name of the constant for easy reading of the dependency type.\n\n### Aliases\nDependency can have an alias for more intuitive access. Keep in mind that dependencies with aliases should go at the end of the list (this is Ruby feature)\n\n```ruby\ninclude Deps['OrderMailer', product_repo: 'Warehouse::ProductRepository']\n\nproduct_repo # Warehouse::ProductRepository\nOrderMailer() # OrderMailer\n```\n\n### Tests (Rspec)\n\n#### setup\nFor dependency testing, add the following to Rspec setup\n\n# spec/rails_helper.rb\n```ruby\n# ...\nrequire 'active_dry_deps/rspec'\nrequire 'active_dry_deps/stub'\n\nDeps.enable_stubs!\n\nRSpec.configure do |config|\n  config.after(:each) { Deps.reset }\nend\n```\n\n#### deps\nThe gem adds Rspec matcher `deps` for stub dependency\n\n```ruby\nDeps.register('order.dependency', Class.new { def self.call = 'failure' })\n\nlet(:service_klass) do\n  Class.new do\n    include Deps['Order::Dependency.call']\n\n    def call = Dependency()\n  end\nend\n\nit 'failure' do\n  expect(service_klass.new.call).to be 'failure'\nend\n\nit 'success' do\n  service = service_klass.new\n  expect(service).to deps(Dependency: 'success')\n\n  expect(service.call).to be 'success'\nend\n```\n\n#### stub, unstub, reset\nDependency can be stubbed at the container level. This allows to override all calls to it\n\n```ruby\nit 'stub' do\n  Deps.stub('Order::Dependency', double(call: 'success'))\n  expect(service_klass.new.call).to be 'success'\n\n  Deps.unstub('Order::Dependency') # or Deps.reset for unsub all keys\n  expect(service_klass.new.call).to be 'failure'\nend\n```\n\n#### global_stub, global_unstub\nSometimes it is necessary to stub dependencies for all or almost all tests\n\n# spec/rails_helper.rb\n```ruby\n# ...\nDeps.enable_stubs!\n\nDeps.global_stub('PushService', Class.new { def self.call = 'global-stub-push' })\n```\n\nDependency stubbed with `global_stub` may be restored only with `global_unstub`. You can unstub dependency when it really needed and ignore in all other cases \n\n```ruby\nit 'sends webpush' do\n  Deps.global_unstub('PushService')\n  \n  # expect(PushService.call).to ...\nend\n```\n\n*`Deps.global_stub` should not be used within examples*\n\n## Configuration\nThe gem is auto-configuring, but you can override settings\n\n```ruby\n# config/initializers/active_dry_deps.rb\nActiveDryDeps.configure do |config|\n  config.inflector = ActiveSupport::Inflector\n  config.inject_global_constant = 'Deps'\nend\n```\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/corp-gp/active_dry_deps.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcorp-gp%2Factive_dry_deps","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcorp-gp%2Factive_dry_deps","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcorp-gp%2Factive_dry_deps/lists"}