├── spec ├── dummy │ ├── log │ │ └── .keep │ ├── .rspec │ ├── lib │ │ └── mailer.rb │ ├── app │ │ ├── operations │ │ │ └── create_user.rb │ │ ├── workers │ │ │ └── mailer_worker.rb │ │ ├── models │ │ │ └── user_repo.rb │ │ └── controllers │ │ │ └── application_controller.rb │ ├── Gemfile │ └── config │ │ ├── spring.rb │ │ ├── boot.rb │ │ ├── routes.rb │ │ ├── environment.rb │ │ ├── system │ │ └── boot │ │ │ └── persistence.rb │ │ ├── initializers │ │ ├── session_store.rb │ │ ├── mime_types.rb │ │ ├── application_controller_renderer.rb │ │ ├── filter_parameter_logging.rb │ │ ├── cookies_serializer.rb │ │ ├── container.rb │ │ ├── wrap_parameters.rb │ │ ├── backtrace_silencers.rb │ │ ├── inflections.rb │ │ └── new_framework_defaults.rb │ │ ├── application.rb │ │ ├── locales │ │ └── en.yml │ │ ├── secrets.yml │ │ └── environments │ │ ├── development.rb │ │ ├── test.rb │ │ └── production.rb ├── integration │ ├── container │ │ └── using_plugins_spec.rb │ ├── models │ │ └── user_repo_spec.rb │ ├── environments_spec.rb │ ├── railtie_spec.rb │ └── container_spec.rb └── spec_helper.rb ├── .rspec ├── lib ├── dry-system-rails.rb └── dry │ └── system │ ├── rails │ ├── version.rb │ ├── container.rb │ └── railtie.rb │ └── rails.rb ├── .gitignore ├── Gemfile ├── .codeclimate.yml ├── Rakefile ├── .travis.yml ├── dry-system-rails.gemspec ├── .rubocop.yml ├── LICENSE ├── CHANGELOG.md ├── CONTRIBUTING.md └── README.md /spec/dummy/log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/dummy/.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /spec/dummy/lib/mailer.rb: -------------------------------------------------------------------------------- 1 | class Mailer 2 | end -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | --order random 4 | --tag ~production_env 5 | -------------------------------------------------------------------------------- /lib/dry-system-rails.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'dry/system/rails' 4 | -------------------------------------------------------------------------------- /spec/dummy/app/operations/create_user.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Operations 4 | class CreateUser 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/dummy/app/workers/mailer_worker.rb: -------------------------------------------------------------------------------- 1 | module Workers 2 | class MailerWorker 3 | include Dummy::Import['mailer'] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/dummy/app/models/user_repo.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class UserRepo 4 | include Dummy::Import['persistence.db'] 5 | end 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | coverage 3 | /.bundle 4 | vendor/bundle 5 | bin/ 6 | tmp/ 7 | .idea/ 8 | Gemfile.lock 9 | .byebug_history 10 | spec/dummy/.bundle 11 | -------------------------------------------------------------------------------- /lib/dry/system/rails/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Dry 4 | module System 5 | module Rails 6 | VERSION = '0.3.1' 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/dummy/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'railties' 6 | gem 'actionpack' 7 | gem 'listen' 8 | gem 'dry-system-rails', path: '../..' 9 | -------------------------------------------------------------------------------- /spec/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationController < ActionController::Base 4 | protect_from_forgery with: :exception 5 | end 6 | -------------------------------------------------------------------------------- /spec/dummy/config/spring.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | %w( 4 | .ruby-version 5 | .rbenv-vars 6 | tmp/restart.txt 7 | tmp/caching-dev.txt 8 | ).each { |path| Spring.watch(path) } 9 | -------------------------------------------------------------------------------- /spec/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 4 | 5 | require 'bundler/setup' # Set up gems listed in the Gemfile. 6 | -------------------------------------------------------------------------------- /spec/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.routes.draw do 4 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html 5 | end 6 | -------------------------------------------------------------------------------- /spec/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Load the Rails application. 4 | require_relative 'application' 5 | 6 | # Initialize the Rails application. 7 | Rails.application.initialize! 8 | -------------------------------------------------------------------------------- /spec/dummy/config/system/boot/persistence.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Dummy::Container.boot(:persistence) do |container| 4 | start do 5 | container.register("persistence.db", :i_am_db) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | Rails.application.config.session_store :cookie_store, key: '_dummy_session' 6 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Add new mime types for use in respond_to blocks: 6 | # Mime::Type.register "text/richtext", :rtf 7 | -------------------------------------------------------------------------------- /spec/integration/container/using_plugins_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe 'Using dry-system plugins' do 2 | specify 'Using dry-system plugins (which add extra settings) inside the initializer container block' do 3 | expect(Dummy::Container.env).to eq Rails.env.to_sym 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # ApplicationController.renderer.defaults.merge!( 6 | # http_host: 'example.org', 7 | # https: false 8 | # ) 9 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Configure sensitive parameters which will be filtered from the log file. 6 | Rails.application.config.filter_parameters += [:password] 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gemspec 6 | 7 | group :test do 8 | gem 'railties' 9 | gem 'actionpack' 10 | gem 'simplecov', require: false, platform: :mri 11 | end 12 | 13 | group :tools do 14 | gem 'byebug', platform: :mri 15 | end 16 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | prepare: 4 | fetch: 5 | - url: "https://raw.githubusercontent.com/dry-rb/devtools/master/.rubocop.yml" 6 | path: ".rubocop.yml" 7 | 8 | exclude_patterns: 9 | - "benchmarks/" 10 | - "examples/" 11 | - "spec/" 12 | 13 | plugins: 14 | rubocop: 15 | enabled: true 16 | -------------------------------------------------------------------------------- /spec/integration/models/user_repo_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'user_repo' 4 | 5 | RSpec.describe UserRepo do 6 | subject(:user_repo) { UserRepo.new } 7 | 8 | it 'has persistence.db injected in' do 9 | expect(user_repo.db).to be(Dummy::Container['persistence.db']) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'boot' 4 | 5 | require 'rails' 6 | require 'action_controller/railtie' 7 | 8 | Bundler.setup(*Rails.groups) 9 | 10 | module Dummy 11 | class Application < Rails::Application 12 | config.root = Pathname(__dir__).join('..') 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Specify a serializer for the signed and encrypted cookie jars. 6 | # Valid options are :json, :marshal, and :hybrid. 7 | Rails.application.config.action_dispatch.cookies_serializer = :json 8 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # frozen_string_literal: true 3 | 4 | require 'bundler/gem_tasks' 5 | 6 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib')) 7 | 8 | require 'rspec/core' 9 | require 'rspec/core/rake_task' 10 | 11 | task default: :spec 12 | 13 | desc 'Run all specs in spec directory' 14 | RSpec::Core::RakeTask.new(:spec) 15 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/container.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'dry/system/rails' 4 | 5 | Dry::System::Rails.container do 6 | use :env, inferrer: -> { Rails.env.to_sym } 7 | 8 | config.auto_register << 'lib' << 'app/operations' 9 | 10 | auto_register!('app/workers') do |config| 11 | config.memoize = true 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/integration/environments_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe 'Environments' do 4 | subject { Dummy::Container } 5 | 6 | describe "frozen?" do 7 | context 'when Rails environment is test' do 8 | it { is_expected.not_to be_frozen } 9 | end 10 | 11 | context 'when Rails environment is not test', :production_env do 12 | it { is_expected.to be_frozen } 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # This file contains settings for ActionController::ParamsWrapper which 6 | # is enabled by default. 7 | 8 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 9 | ActiveSupport.on_load(:action_controller) do 10 | wrap_parameters format: [:json] 11 | end 12 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 6 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 7 | 8 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 9 | # Rails.backtrace_cleaner.remove_silencers! 10 | -------------------------------------------------------------------------------- /spec/integration/railtie_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe Dry::System::Rails::Railtie do 4 | subject(:railtie) do 5 | Dry::System::Rails::Railtie.instance 6 | end 7 | 8 | describe '.finalize!' do 9 | it 'reloads container and import module' do 10 | Dummy::Container.register('foo', Object.new) 11 | 12 | Rails.application.reloader.reload! 13 | 14 | expect(Dummy::Container.keys).to_not include('foo') 15 | 16 | klass = Class.new do 17 | include Dummy::Import['operations.create_user'] 18 | end 19 | 20 | obj = klass.new 21 | 22 | expect(obj.create_user).to be_instance_of(Operations::CreateUser) 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Add new inflection rules using the following format. Inflections 6 | # are locale specific, and you may define rules for as many different 7 | # locales as you wish. All of these examples are active by default: 8 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 9 | # inflect.plural /^(ox)$/i, '\1en' 10 | # inflect.singular /^(ox)en/i, '\1' 11 | # inflect.irregular 'person', 'people' 12 | # inflect.uncountable %w( fish sheep ) 13 | # end 14 | 15 | # These inflection rules are supported but not enabled by default: 16 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 17 | # inflect.acronym 'RESTful' 18 | # end 19 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | if RUBY_ENGINE == 'ruby' && ENV['COVERAGE'] == 'true' 4 | require 'yaml' 5 | rubies = YAML.safe_load(File.read(File.join(__dir__, '..', '.travis.yml')))['rvm'] 6 | latest_mri = rubies.select { |v| v =~ /\A\d+\.\d+.\d+\z/ }.max 7 | 8 | if RUBY_VERSION == latest_mri 9 | require 'simplecov' 10 | SimpleCov.start do 11 | add_filter '/spec/' 12 | end 13 | end 14 | end 15 | 16 | begin 17 | require 'byebug' 18 | rescue LoadError; end 19 | 20 | require 'dry-system-rails' 21 | 22 | SPEC_ROOT = Pathname(__dir__) 23 | 24 | Dir[SPEC_ROOT.join('shared/**/*.rb')].each(&method(:require)) 25 | Dir[SPEC_ROOT.join('support/**/*.rb')].each(&method(:require)) 26 | 27 | ENV['RAILS_ENV'] ||= 'test' 28 | require SPEC_ROOT.join('dummy/config/environment') 29 | 30 | RSpec.configure(&:disable_monkey_patching!) 31 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/new_framework_defaults.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | # 5 | # This file contains migration options to ease your Rails 5.0 upgrade. 6 | # 7 | # Read the Rails 5.0 release notes for more info on each option. 8 | 9 | # Enable per-form CSRF tokens. Previous versions had false. 10 | Rails.application.config.action_controller.per_form_csrf_tokens = true 11 | 12 | # Enable origin-checking CSRF mitigation. Previous versions had false. 13 | Rails.application.config.action_controller.forgery_protection_origin_check = true 14 | 15 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. 16 | # Previous versions had false. 17 | ActiveSupport.to_time_preserves_timezone = true 18 | 19 | # Configure SSL options to enable HSTS with subdomains. Previous versions had false. 20 | Rails.application.config.ssl_options = { hsts: { subdomains: true } } 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | cache: bundler 3 | bundler_args: --without console 4 | before_script: 5 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 6 | - chmod +x ./cc-test-reporter 7 | - ./cc-test-reporter before-build 8 | after_script: 9 | - "[ -d coverage ] && ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT" 10 | script: 11 | - bundle exec rake spec 12 | - RAILS_ENV=production bundle exec rspec --tag production_env 13 | rvm: 14 | - 2.6.3 15 | - 2.5.5 16 | - 2.4.6 17 | - truffleruby 18 | matrix: 19 | allow_failures: 20 | - rvm: truffleruby 21 | notifications: 22 | email: false 23 | webhooks: 24 | urls: 25 | - https://webhooks.gitter.im/e/19098b4253a72c9796db 26 | on_success: change # options: [always|never|change] default: always 27 | on_failure: always # options: [always|never|change] default: always 28 | on_start: false # default: false 29 | -------------------------------------------------------------------------------- /dry-system-rails.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # frozen_string_literal: true 3 | 4 | require File.expand_path('../lib/dry/system/rails/version', __FILE__) 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'dry-system-rails' 8 | spec.version = Dry::System::Rails::VERSION 9 | spec.authors = ['Piotr Solnica'] 10 | spec.email = ['piotr.solnica@gmail.com'] 11 | spec.summary = 'Railtie for dry-system' 12 | spec.homepage = 'https://github.com/dry-rb/dry-system-rails' 13 | spec.license = 'MIT' 14 | 15 | spec.files = `git ls-files -z`.split("\x0") 16 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 17 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 18 | spec.require_paths = ['lib'] 19 | spec.required_ruby_version = '~> 2.4' 20 | 21 | spec.add_runtime_dependency 'dry-system', '~> 0.12' 22 | 23 | spec.add_development_dependency 'bundler' 24 | spec.add_development_dependency 'rake' 25 | spec.add_development_dependency 'rspec' 26 | end 27 | -------------------------------------------------------------------------------- /spec/dummy/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 93cc56a5c8991ad66f5a0ad2fa901a680d33b4d2b2556737115113246700705a46122df56d060d3da333778569e9bd5a0a0e8c21693cd10c48ad496db33edffd 15 | 16 | test: 17 | secret_key_base: 3040bb7ecbe834a675144408fc988bdaa791f78e9a9df2493665d514e25b8e409644e65194e070f903d18a03cd01e9463d0d562bb50bfbb7ba9dc8d0aef2076b 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | TargetRubyVersion: 2.4 3 | 4 | Style/EachWithObject: 5 | Enabled: false 6 | 7 | Style/StringLiterals: 8 | Enabled: true 9 | EnforcedStyle: single_quotes 10 | 11 | Style/Alias: 12 | Enabled: false 13 | 14 | Style/LambdaCall: 15 | Enabled: false 16 | 17 | Style/StabbyLambdaParentheses: 18 | Enabled: false 19 | 20 | Style/FormatString: 21 | Enabled: false 22 | 23 | Layout/SpaceInLambdaLiteral: 24 | Enabled: false 25 | 26 | Layout/MultilineMethodCallIndentation: 27 | Enabled: true 28 | EnforcedStyle: indented 29 | 30 | Metrics/LineLength: 31 | Max: 100 32 | 33 | Metrics/MethodLength: 34 | Max: 22 35 | 36 | Metrics/ClassLength: 37 | Max: 150 38 | 39 | Metrics/AbcSize: 40 | Max: 20 41 | 42 | Metrics/BlockLength: 43 | Enabled: true 44 | Exclude: 45 | - 'spec/**/*_spec.rb' 46 | 47 | Metrics/CyclomaticComplexity: 48 | Enabled: true 49 | Max: 10 50 | 51 | Lint/BooleanSymbol: 52 | Enabled: false 53 | 54 | Style/AccessModifierDeclarations: 55 | Enabled: false 56 | 57 | Style/BlockDelimiters: 58 | EnforcedStyle: semantic 59 | 60 | -------------------------------------------------------------------------------- /spec/integration/container_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe 'Application container' do 4 | subject(:system) { Dummy::Container } 5 | 6 | describe '#load_component' do 7 | it 'loads component by its identifier' do 8 | system.load_component('user_repo') 9 | 10 | expect(Object.const_defined?(:UserRepo)).to be(true) 11 | end 12 | end 13 | 14 | describe '#[]' do 15 | it 'returns auto-registered component' do 16 | expect(system['operations.create_user']).to be_instance_of(Operations::CreateUser) 17 | end 18 | end 19 | 20 | describe '#auto_register!' do 21 | it 'auto-registers files based on config' do 22 | Dummy::Container.finalize!(freeze: false) # force auto-registration to run (since we're in test env) 23 | 24 | mailer_worker = Dummy::Container['workers.mailer_worker'] 25 | 26 | expect(mailer_worker).to be_instance_of(Workers::MailerWorker) 27 | expect(Dummy::Container['workers.mailer_worker']).to be(mailer_worker) # memoized 28 | expect(mailer_worker.mailer).to be_instance_of(Mailer) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 dry-rb.org team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # In the development environment your application's code is reloaded on 7 | # every request. This slows down response time but is perfect for development 8 | # since you don't have to restart the web server when you make code changes. 9 | config.cache_classes = false 10 | 11 | # Do not eager load code on boot. 12 | config.eager_load = false 13 | 14 | # Show full error reports. 15 | config.consider_all_requests_local = true 16 | 17 | # Enable/disable caching. By default caching is disabled. 18 | if Rails.root.join('tmp/caching-dev.txt').exist? 19 | config.action_controller.perform_caching = true 20 | 21 | config.cache_store = :memory_store 22 | config.public_file_server.headers = { 23 | 'Cache-Control' => 'public, max-age=172800' 24 | } 25 | else 26 | config.action_controller.perform_caching = false 27 | 28 | config.cache_store = :null_store 29 | end 30 | 31 | # Print deprecation notices to the Rails logger. 32 | config.active_support.deprecation = :log 33 | 34 | 35 | # Raises error for missing translations 36 | # config.action_view.raise_on_missing_translations = true 37 | 38 | # Use an evented file watcher to asynchronously detect changes in source code, 39 | # routes, locales, etc. This feature depends on the listen gem. 40 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 41 | end 42 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v0.3.1 2019-10-09 2 | 3 | ### Changed 4 | 5 | - Do not finalize container when application is in test mode (timriley) 6 | 7 | [Compare v0.3.0...v0.3.1](https://github.com/dry-rb/dry-system/compare/v0.3.0...v0.3.1) 8 | 9 | # v0.3.0 2019-09-29 10 | 11 | ### Changed 12 | 13 | - Depend on dry-system 0.12.x (arielvalentin) 14 | - Drop support for Ruby versions earlier than 2.4 (arielvalentin) 15 | - Evaluate user-supplied `Dry::System::Rails.container { ... }` block before applying any standard behaviour (which includes accessing the container's config). This change makes it possible for the user to adjust the container's dry-configurable-provided settings, e.g. through using dry-system plugins that add their own settings (timriley) 16 | 17 | [Compare v0.2.0...v0.3.0](https://github.com/dry-rb/dry-system/compare/v0.2.0...v0.3.0) 18 | 19 | # v0.2.0 2019-04-16 20 | 21 | ### Added 22 | 23 | - Support for code reloading in development environment (arielvalentin + solnic) 24 | 25 | ### Changed 26 | 27 | - [BREAKING] Initializer interface is now `Dry::System::Rails.container { ... }` which simply captures the block 28 | to evaluate it in the context of the container class. This gives you full control over container creation (solnic) 29 | 30 | # v0.1.0 2018-01-05 31 | 32 | ### Changed 33 | 34 | * Upgraded to `dry-system` `v0.10.1` 35 | 36 | # v0.0.2 2016-08-23 37 | 38 | ### Fixed 39 | 40 | * `Railtie#namespace` has been renamed to `#app_namespace` because it may conflict with Rake's '#namespace' (blelump) 41 | 42 | # v0.0.1 2016-08-17 43 | 44 | First public release 45 | -------------------------------------------------------------------------------- /lib/dry/system/rails.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'dry/system/rails/railtie' 4 | require 'dry/system/rails/container' 5 | 6 | module Dry 7 | module System 8 | # Initializer interface 9 | # 10 | # @example set up a customized container 11 | # # config/initializer/system.rb 12 | # 13 | # Dry::System::Rails.container do 14 | # use :monitoring 15 | # 16 | # config.auto_register << 'app/operations' 17 | # end 18 | # 19 | # @api public 20 | module Rails 21 | # Set container block that will be evaluated in the context of the container 22 | # 23 | # @api public 24 | def self.container(&block) 25 | @container_block = block 26 | self 27 | end 28 | 29 | # Create a new container class 30 | # 31 | # This is used during booting and reloading 32 | # 33 | # @api private 34 | def self.create_container(options = {}) 35 | container = Class.new(Container) 36 | 37 | container.class_eval(&@container_block) if container_block 38 | 39 | default_options = { 40 | root: ::Rails.root, 41 | system_dir: ::Rails.root.join('config/system'), 42 | } 43 | container.config.update(default_options.merge(options)) 44 | 45 | container.load_paths!('lib', 'app', 'app/models') 46 | 47 | container 48 | end 49 | 50 | # @api private 51 | def self.container_block 52 | defined?(@container_block) && @container_block 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/dry/system/rails/container.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'dry/system/container' 4 | 5 | module Dry 6 | module System 7 | module Rails 8 | # Customized Container class for Rails application 9 | # 10 | # @api public 11 | class Container < System::Container 12 | setting :auto_register_configs, [], &:dup 13 | 14 | class << self 15 | # Auto register files from the provided directory 16 | # 17 | # @api public 18 | def auto_register!(dir, &block) 19 | if block 20 | config.auto_register_configs << [dir, block] 21 | else 22 | config.auto_register << dir 23 | end 24 | 25 | self 26 | end 27 | 28 | # @api private 29 | def finalize!(options = {}) 30 | config.auto_register_configs.each do |(dir, block)| 31 | auto_registrar.call(dir, &block) 32 | end 33 | super 34 | end 35 | 36 | # Use `require_dependency` to make code reloading work 37 | # 38 | # @api private 39 | def require_path(path) 40 | require_dependency(path) 41 | end 42 | 43 | # This is called when reloading in dev mode 44 | # 45 | # @api private 46 | def refresh_boot_files 47 | booter.boot_files.each do |boot_file| 48 | load(boot_file) 49 | end 50 | self 51 | end 52 | end 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Issue Guidelines 2 | 3 | ## Reporting bugs 4 | 5 | If you found a bug, report an issue and describe what's the expected behavior versus what actually happens. If the bug causes a crash, attach a full backtrace. If possible, a reproduction script showing the problem is highly appreciated. 6 | 7 | ## Reporting feature requests 8 | 9 | Report a feature request **only after discussing it first on [discuss.dry-rb.org](https://discuss.dry-rb.org)** where it was accepted. Please provide a concise description of the feature, don't link to a discussion thread, and instead summarize what was discussed. 10 | 11 | ## Reporting questions, support requests, ideas, concerns etc. 12 | 13 | **PLEASE DON'T** - use [discuss.dry-rb.org](http://discuss.dry-rb.org) instead. 14 | 15 | # Pull Request Guidelines 16 | 17 | A Pull Request will only be accepted if it addresses a specific issue that was reported previously, or fixes typos, mistakes in documentation etc. 18 | 19 | Other requirements: 20 | 21 | 1) Do not open a pull request if you can't provide tests along with it. If you have problems writing tests, ask for help in the related issue. 22 | 2) Follow the style conventions of the surrounding code. In most cases, this is standard ruby style. 23 | 3) Add API documentation if it's a new feature 24 | 4) Update API documentation if it changes an existing feature 25 | 5) Bonus points for sending a PR to [github.com/dry-rb/dry-rb.org](github.com/dry-rb/dry-rb.org) which updates user documentation and guides 26 | 27 | # Asking for help 28 | 29 | If these guidelines aren't helpful, and you're stuck, please post a message on [discuss.dry-rb.org](https://discuss.dry-rb.org). 30 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # The test environment is used exclusively to run your application's 7 | # test suite. You never need to work with it otherwise. Remember that 8 | # your test database is "scratch space" for the test suite and is wiped 9 | # and recreated between test runs. Don't rely on the data there! 10 | config.cache_classes = true 11 | 12 | # Do not eager load code on boot. This avoids loading your whole application 13 | # just for the purpose of running a single test. If you are using a tool that 14 | # preloads Rails for running tests, you may have to set it to true. 15 | config.eager_load = false 16 | 17 | # Configure public file server for tests with Cache-Control for performance. 18 | config.public_file_server.enabled = true 19 | config.public_file_server.headers = { 20 | 'Cache-Control' => 'public, max-age=3600' 21 | } 22 | 23 | # Show full error reports and disable caching. 24 | config.consider_all_requests_local = true 25 | config.action_controller.perform_caching = false 26 | 27 | # Raise exceptions instead of rendering exception templates. 28 | config.action_dispatch.show_exceptions = false 29 | 30 | # Disable request forgery protection in test environment. 31 | config.action_controller.allow_forgery_protection = false 32 | 33 | # Print deprecation notices to the stderr. 34 | config.active_support.deprecation = :stderr 35 | 36 | # Raises error for missing translations 37 | # config.action_view.raise_on_missing_translations = true 38 | end 39 | -------------------------------------------------------------------------------- /lib/dry/system/rails/railtie.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails/railtie' 4 | 5 | module Dry 6 | module System 7 | module Rails 8 | # System railtie is responsible for setting up a container and handling reloading in dev mode 9 | # 10 | # @api private 11 | class Railtie < ::Rails::Railtie 12 | config.to_prepare do 13 | Railtie.finalize! 14 | end 15 | 16 | # @api private 17 | def finalize! 18 | container = System::Rails.create_container(name: name) 19 | 20 | set_or_reload(:Container, container) 21 | set_or_reload(:Import, container.injector) 22 | 23 | container.refresh_boot_files if reloading? 24 | 25 | container.finalize! unless ::Rails.env.test? 26 | end 27 | 28 | # @api private 29 | def reloading? 30 | app_namespace.const_defined?(:Container) 31 | end 32 | 33 | # @api private 34 | def name 35 | app_namespace.name.underscore.to_sym 36 | end 37 | 38 | # TODO: we had to rename namespace=>app_namespace because 39 | # Rake::DSL's Kernel#namespace *sometimes* breaks things. 40 | # Currently we are missing specs verifying that rake tasks work 41 | # correctly and those must be added! 42 | # 43 | # @api private 44 | def app_namespace 45 | @app_namespace ||= begin 46 | top_level_namespace = ::Rails.application.class.to_s.split("::").first 47 | Object.const_get(top_level_namespace) 48 | end 49 | end 50 | 51 | # @api private 52 | def set_or_reload(const_name, const) 53 | if app_namespace.const_defined?(const_name) 54 | app_namespace.__send__(:remove_const, const_name) 55 | end 56 | 57 | app_namespace.const_set(const_name, const) 58 | end 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # Code is not reloaded between requests. 7 | config.cache_classes = true 8 | 9 | # Eager load code on boot. This eager loads most of Rails and 10 | # your application in memory, allowing both threaded web servers 11 | # and those relying on copy on write to perform better. 12 | # Rake tasks automatically ignore this option for performance. 13 | config.eager_load = true 14 | 15 | # Full error reports are disabled and caching is turned on. 16 | config.consider_all_requests_local = false 17 | config.action_controller.perform_caching = true 18 | 19 | # Disable serving static files from the `/public` folder by default since 20 | # Apache or NGINX already handles this. 21 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 22 | 23 | 24 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 25 | # config.action_controller.asset_host = 'http://assets.example.com' 26 | 27 | # Specifies the header that your server uses for sending files. 28 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 29 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 30 | 31 | 32 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 33 | # config.force_ssl = true 34 | 35 | # Use the lowest log level to ensure availability of diagnostic information 36 | # when problems arise. 37 | config.log_level = :debug 38 | 39 | # Prepend all log lines with the following tags. 40 | config.log_tags = [ :request_id ] 41 | 42 | # Use a different cache store in production. 43 | # config.cache_store = :mem_cache_store 44 | 45 | # Use a real queuing backend for Active Job (and separate queues per environment) 46 | # config.active_job.queue_adapter = :resque 47 | # config.active_job.queue_name_prefix = "dummy_#{Rails.env}" 48 | 49 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 50 | # the I18n.default_locale when a translation cannot be found). 51 | config.i18n.fallbacks = true 52 | 53 | # Send deprecation notices to registered listeners. 54 | config.active_support.deprecation = :notify 55 | 56 | # Use default logging formatter so that PID and timestamp are not suppressed. 57 | config.log_formatter = ::Logger::Formatter.new 58 | 59 | # Use a different logger for distributed setups. 60 | # require 'syslog/logger' 61 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 62 | 63 | if ENV["RAILS_LOG_TO_STDOUT"].present? 64 | logger = ActiveSupport::Logger.new(STDOUT) 65 | logger.formatter = config.log_formatter 66 | config.logger = ActiveSupport::TaggedLogging.new(logger) 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [gem]: https://rubygems.org/gems/dry-system-rails 2 | [travis]: https://travis-ci.org/dry-rb/dry-system-rails 3 | [codeclimate]: https://codeclimate.com/github/dry-rb/dry-system-rails 4 | [chat]: https://dry-rb.zulipchat.com 5 | [inchpages]: http://inch-ci.org/github/dry-rb/dry-system-rails 6 | 7 | # dry-system-rails [![Join the chat at https://dry-rb.zulipchat.com](https://img.shields.io/badge/dry--rb-join%20chat-%23346b7a.svg)][chat] 8 | 9 | [![Gem Version](https://badge.fury.io/rb/dry-system-rails.svg)][gem] 10 | [![Build Status](https://travis-ci.org/dry-rb/dry-system-rails.svg?branch=master)][travis] 11 | [![Code Climate](https://codeclimate.com/github/dry-rb/dry-system-rails/badges/gpa.svg)][codeclimate] 12 | [![Test Coverage](https://codeclimate.com/github/dry-rb/dry-system-rails/badges/coverage.svg)][codeclimate] 13 | [![Inline docs](http://inch-ci.org/github/dry-rb/dry-system-rails.svg?branch=master)][inchpages] 14 | 15 | Railtie for dry-system (つ◕౪◕)つ━☆゚.*・。゚ 16 | 17 | ## Installation 18 | 19 | Add it to your Gemfile: 20 | 21 | ``` 22 | gem 'dry-system-rails' 23 | ``` 24 | 25 | ## Usage 26 | 27 | To configure auto-registration create `config/initializers/system.rb` with the following content: 28 | 29 | ``` ruby 30 | Dry::System::Rails.container do 31 | # you can set it to whatever you want and add as many dirs you want 32 | config.auto_register << 'lib' 33 | end 34 | ``` 35 | 36 | The `Dry::System::Rails::Railtie` creates a container and injector on your behalf at runtime and assign them to two constants `Container` and `Import` 37 | under your applications root module. E.g. if your application is named `MyApp`, the `Railtie` will add the following constants: 38 | 39 | * `MyApp::Container` 40 | * `MyApp::Import` 41 | 42 | Now you can use `MyApp::Import` to inject components into your objects and framework components: 43 | 44 | ``` ruby 45 | # lib/user_repo.rb 46 | class UserRepo 47 | 48 | end 49 | 50 | # lib/create_user.rb 51 | class CreateUser 52 | include MyApp::Import['user_repo'] 53 | end 54 | 55 | # app/controllers/users_controller.rb 56 | class UsersController < ApplicationController 57 | include MyApp::Import['create_user'] 58 | end 59 | ``` 60 | 61 | ## Working with Framework Dependencies 62 | 63 | The Rails API is designed around the usage of class methods. If you choose to write domain logic in objects you will likely encounter a situation where your code will have to use one of the framework components. That is where manual registration using [bootable dependency](https://dry-rb.org/gems/dry-system/booting) will come in handy. 64 | 65 | E.g. You have an object `CreateWidget` that needs to process widgets asynchronously with an `Widgets:NotificationJob` but you want to leverage dependency injection to decouple the components: 66 | 67 | ```ruby 68 | # config/initializers/system.rb 69 | Dry::System::Rails.container do 70 | # changes `self` to the container 71 | config.auto_register << 'lib' 72 | end 73 | 74 | # app/jobs/widgets/notification_job.rb 75 | class Widgets::NotificationJob < ApplicationJob 76 | end 77 | 78 | # config/system/boot/application.rb 79 | # Use bootable componets to manually register framework dependencies 80 | MyApp::Container.boot(:application) do |app| 81 | setup do 82 | app.namespace(:widgets) do |widgets| 83 | widgets.register(:notification, memoize: true) { Widgets::NotificationJob } 84 | end 85 | end 86 | end 87 | 88 | # lib/create_widget.rb 89 | class CreateWidget 90 | include MyApp::Import[job: 'widgets.notification'] 91 | 92 | def call(args) 93 | # some logic that creates a widget command 94 | job.perform_later(create_widget_command) 95 | end 96 | end 97 | ``` 98 | 99 | ## TODO 100 | 101 | This is super alpha and it's missing a couple of things: 102 | 103 | * Some generators to make UX nicer 104 | * Tests for loading scripts (console etc) 105 | * Tests for running rake tasks 106 | 107 | ## License 108 | 109 | See `LICENSE` file. 110 | --------------------------------------------------------------------------------