dry-system 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -4
  3. data/CONTRIBUTING.md +29 -0
  4. data/examples/custom_configuration_auto_register/Gemfile +5 -0
  5. data/examples/custom_configuration_auto_register/lib/entities/user.rb +7 -0
  6. data/examples/custom_configuration_auto_register/lib/user_repo.rb +5 -0
  7. data/examples/custom_configuration_auto_register/run.rb +8 -0
  8. data/examples/custom_configuration_auto_register/system/boot/persistence.rb +13 -0
  9. data/examples/custom_configuration_auto_register/system/container.rb +16 -0
  10. data/examples/custom_configuration_auto_register/system/import.rb +3 -0
  11. data/lib/dry/system/auto_registrar.rb +25 -15
  12. data/lib/dry/system/auto_registrar/configuration.rb +39 -0
  13. data/lib/dry/system/booter.rb +10 -5
  14. data/lib/dry/system/component.rb +8 -12
  15. data/lib/dry/system/constants.rb +1 -0
  16. data/lib/dry/system/container.rb +36 -15
  17. data/lib/dry/system/errors.rb +8 -8
  18. data/lib/dry/system/magic_comments_parser.rb +31 -0
  19. data/lib/dry/system/manual_registrar.rb +57 -0
  20. data/lib/dry/system/version.rb +1 -1
  21. data/spec/fixtures/components/no_register.rb +4 -0
  22. data/spec/fixtures/magic_comments/comments.rb +17 -0
  23. data/spec/fixtures/manual_registration/container/foo.rb +6 -0
  24. data/spec/fixtures/manual_registration/lib/test/foo.rb +9 -0
  25. data/spec/fixtures/multiple_namespaced_components/multiple/level/baz.rb +7 -0
  26. data/spec/fixtures/multiple_namespaced_components/multiple/level/foz.rb +6 -0
  27. data/spec/fixtures/other/config/boot/hell.rb +3 -0
  28. data/spec/fixtures/test/system/boot/hell.rb +3 -0
  29. data/spec/integration/container/lazy_loading/manual_registration_spec.rb +18 -0
  30. data/spec/integration/import_spec.rb +2 -2
  31. data/spec/unit/auto_registrar/configuration_spec.rb +26 -0
  32. data/spec/unit/component_spec.rb +0 -5
  33. data/spec/unit/container/auto_register_spec.rb +34 -6
  34. data/spec/unit/container/import_spec.rb +5 -22
  35. data/spec/unit/container_spec.rb +8 -0
  36. data/spec/unit/magic_comments_parser_spec.rb +41 -0
  37. metadata +36 -5
  38. data/.rubocop.yml +0 -34
  39. data/.rubocop_todo.yml +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '085640c18c734b66700738f731ea66d0001cdc83'
4
- data.tar.gz: a6c6835df5df05045cddbc41615a581c11c4ea1d
3
+ metadata.gz: '0793e53e85b2a2fa0ea7da93bb11734362f87cd9'
4
+ data.tar.gz: 9d0cc9e0989fe9f071468e4e6b310a3fc009b06f
5
5
  SHA512:
6
- metadata.gz: 415e4f12b22eeb6e7f7b9f6f856b21953f1126b634d82cc17e9c3da3eab1f6aff30c261fddde7401495d6bccd1092fa7185a772031abf886732d913b1182aeee
7
- data.tar.gz: 3082441096b4e4208f1db30d817a816a809026b3cd07886b417edfc77be235167c54cdd9e5de4336109b88daa2633cca1c122a3f8fcbac8cea6421a099a0c633
6
+ metadata.gz: 50818887822ec2a8130910a359d45690312cd10c28b1537d5dbf0a45f98eb2f1abc0d70bf6f709313a939927a22c2d8e29fc8f2aa5806f53206d0b6390c37b6d
7
+ data.tar.gz: 1a350ffcba4a4bd7fca37be0976a55711aad5feaa70d493280aa477d4ea0723d5092fa35031c71c2b1d1ab6adaff6054d2a24e12fbdf4517b6eadb77e2850fe7
data/CHANGELOG.md CHANGED
@@ -1,13 +1,47 @@
1
- # 0.6.0 2016-02-02
1
+ # 0.7.0 - 2017-06-15
2
+
3
+ ### Added
4
+
5
+ - Added `manual_registrar` container setting (along with default `ManualRegistrar` implementation), and `registrations_dir` setting. These provide support for a well-established place for keeping files with manual container registrations (timriley)
6
+ - AutoRegistrar parses initial lines of Ruby source files for "magic comments" when auto-registering components. An `# auto_register: false` magic comment will prevent a Ruby file from being auto-registered (timriley)
7
+ - `Container.auto_register!`, when called with a block, yields a configuration object to control the auto-registration behavior for that path, with support for configuring 2 different aspects of auto-registration behavior (both optional):
8
+
9
+ ```ruby
10
+ class MyContainer < Dry::System::Container
11
+ auto_register!('lib') do |config|
12
+ config.instance do |component|
13
+ # custom logic for initializing a component
14
+ end
15
+
16
+ config.exclude do |component|
17
+ # return true to skip auto-registration of the component, e.g.
18
+ # component.path =~ /entities/
19
+ end
20
+ end
21
+ end
22
+ ```
23
+
24
+ - A helpful error will be raised if a bootable component's finalize block name doesn't match its boot file name (GustavoCaso)
25
+
26
+ ### Changed
27
+
28
+ - The `default_namespace` container setting now supports multi-level namespaces (GustavoCaso)
29
+ - `Container.auto_register!` yields a configuration block instead of a block for returning a custom instance (see above) (GustavoCaso)
30
+ - `Container.import` now requires an explicit local name for the imported container (e.g. `import(local_name: AnotherContainer)`) (timriley)
31
+
32
+ [Compare v0.6.0...v0.7.0](https://github.com/dry-rb/dry-system/compare/v0.6.0...v0.7.0)
33
+
34
+
35
+ # 0.6.0 - 2016-02-02
2
36
 
3
37
  ### Changed
4
38
 
5
39
  * Lazy load components as they are resolved, rather than on injection (timriley)
6
40
  * Perform registration even though component already required (blelump)
7
41
 
8
- [Compare v0.5.0...v0.6.0](https://github.com/dry-rb/dry-system/compare/v0.5.1...v0.6.0)
42
+ [Compare v0.5.1...v0.6.0](https://github.com/dry-rb/dry-system/compare/v0.5.1...v0.6.0)
9
43
 
10
- # 0.5.1 2016-08-23
44
+ # 0.5.1 - 2016-08-23
11
45
 
12
46
  ### Fixed
13
47
 
@@ -15,7 +49,7 @@
15
49
 
16
50
  [Compare v0.5.0...v0.5.1](https://github.com/dry-rb/dry-system/compare/v0.5.0...v0.5.1)
17
51
 
18
- # 0.5.0 2016-08-15
52
+ # 0.5.0 - 2016-08-15
19
53
 
20
54
  This is a major refactoring with better internal APIs and improved support
21
55
  for multi-container setups. As part of this release `dry-system` has been renamed to `dry-system`.
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,29 @@
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).
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'dry-system', path: '../..'
4
+ gem 'sequel'
5
+ gem 'sqlite3'
@@ -0,0 +1,7 @@
1
+ require 'import'
2
+
3
+ module Entities
4
+ class User
5
+ include Import['persistence.db']
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ require 'import'
2
+
3
+ class UserRepo
4
+ include Import['persistence.db']
5
+ end
@@ -0,0 +1,8 @@
1
+ require 'bundler/setup'
2
+ require_relative 'system/container'
3
+
4
+ App.finalize!
5
+
6
+ user_repo = App['user_repo']
7
+ puts "User has not been loaded" unless App.key?('entities.user')
8
+ puts user_repo.db.inspect
@@ -0,0 +1,13 @@
1
+ App.finalize(:persistence) do |persistence|
2
+ init do
3
+ require 'sequel'
4
+ end
5
+
6
+ start do
7
+ persistence.register('persistence.db', Sequel.connect('sqlite::memory'))
8
+ end
9
+
10
+ stop do
11
+ db.close_connection
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ require 'dry/system/container'
2
+
3
+ class App < Dry::System::Container
4
+ load_paths!('lib', 'system')
5
+
6
+ auto_register!('lib') do |config|
7
+ config.instance do |component|
8
+ # some custom initialization logic
9
+ component.instance
10
+ end
11
+
12
+ config.exclude do |component|
13
+ component.path =~ /entities/
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'container'
2
+
3
+ Import = App.injector
@@ -1,4 +1,6 @@
1
1
  require 'dry/system/constants'
2
+ require 'dry/system/magic_comments_parser'
3
+ require 'dry/system/auto_registrar/configuration'
2
4
 
3
5
  module Dry
4
6
  module System
@@ -6,7 +8,7 @@ module Dry
6
8
  #
7
9
  # This is currently configured by default for every System::Container.
8
10
  # Auto-registrar objects are responsible for loading files from configured
9
- # auto-register paths and registering components automatically withing the
11
+ # auto-register paths and registering components automatically within the
10
12
  # container.
11
13
  #
12
14
  # @api private
@@ -26,14 +28,14 @@ module Dry
26
28
  end
27
29
 
28
30
  # @api private
29
- def call(dir, &block)
31
+ def call(dir)
32
+ registration_config = Configuration.new
33
+ yield(registration_config) if block_given?
30
34
  components(dir).each do |component|
35
+ next if !component.auto_register? || registration_config.exclude.(component)
36
+
31
37
  container.require_component(component) do
32
- if block
33
- register(component.identifier, yield(component))
34
- else
35
- register(component.identifier) { component.instance }
36
- end
38
+ register(component.identifier) { registration_config.instance.(component) }
37
39
  end
38
40
  end
39
41
  end
@@ -42,23 +44,31 @@ module Dry
42
44
 
43
45
  # @api private
44
46
  def components(dir)
45
- paths(dir).
46
- map { |path| component(path) }.
47
+ files(dir).
48
+ map { |file_name| [file_name, file_options(file_name)] }.
49
+ map { |(file_name, options)| component(relative_path(dir, file_name), **options) }.
47
50
  reject { |component| key?(component.identifier) }
48
51
  end
49
52
 
50
53
  # @api private
51
- def paths(dir)
54
+ def files(dir)
55
+ Dir["#{root}/#{dir}/**/*.rb"]
56
+ end
57
+
58
+ # @api private
59
+ def relative_path(dir, file_path)
52
60
  dir_root = root.join(dir.to_s.split('/')[0])
61
+ file_path.to_s.sub("#{dir_root}/", '').sub(RB_EXT, EMPTY_STRING)
62
+ end
53
63
 
54
- Dir["#{root}/#{dir}/**/*.rb"].map { |path|
55
- path.to_s.sub("#{dir_root}/", '').sub(RB_EXT, EMPTY_STRING)
56
- }
64
+ # @api private
65
+ def file_options(file_name)
66
+ MagicCommentsParser.(file_name)
57
67
  end
58
68
 
59
69
  # @api private
60
- def component(path)
61
- container.component(path)
70
+ def component(path, options)
71
+ container.component(path, options)
62
72
  end
63
73
 
64
74
  # @api private
@@ -0,0 +1,39 @@
1
+ module Dry
2
+ module System
3
+ class AutoRegistrar
4
+ # Default auto_registrar configuration
5
+ #
6
+ # This is currently configured by default for every System::Container.
7
+ # Configuration allows to define custom initializtion logic as well
8
+ # as exclusion, for each component that is been register by Dry::System
9
+ # auto-registration.
10
+ #
11
+ # @api private
12
+ class Configuration
13
+ DEFAULT_INSTANCE = -> component { component.instance }.freeze
14
+ FALSE_PROC = -> * { false }.freeze
15
+
16
+ def self.setting(name)
17
+ define_method(name) do |&block|
18
+ ivar = "@#{name}"
19
+
20
+ if block
21
+ instance_variable_set(ivar, block)
22
+ else
23
+ instance_variable_get(ivar)
24
+ end
25
+ end
26
+ end
27
+
28
+ setting :exclude
29
+ setting :instance
30
+
31
+ # @api private
32
+ def initialize
33
+ @instance = DEFAULT_INSTANCE
34
+ @exclude = FALSE_PROC
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -66,11 +66,11 @@ module Dry
66
66
  def call(name)
67
67
  container, finalizer = finalizers[name]
68
68
 
69
- if finalizer
70
- lifecycle = Lifecycle.new(container, &finalizer)
71
- yield(lifecycle) if block_given?
72
- lifecycle
73
- end
69
+ raise ComponentFileMismatchError.new(name, registered_booted_keys) unless finalizer
70
+
71
+ lifecycle = Lifecycle.new(container, &finalizer)
72
+ yield(lifecycle) if block_given?
73
+ lifecycle
74
74
  end
75
75
 
76
76
  # @api private
@@ -81,6 +81,11 @@ module Dry
81
81
 
82
82
  private
83
83
 
84
+ # @api private
85
+ def registered_booted_keys
86
+ finalizers.keys - booted.keys
87
+ end
88
+
84
89
  # @api private
85
90
  def boot_files
86
91
  path.join('**/*.rb').to_s
@@ -47,9 +47,7 @@ module Dry
47
47
  options = DEFAULT_OPTIONS.merge(options || {})
48
48
 
49
49
  ns, sep = options.values_at(:namespace, :separator)
50
-
51
- ns_name = ensure_valid_namespace(ns, sep)
52
- identifier = ensure_valid_identifier(name, ns_name, sep)
50
+ identifier = ensure_valid_identifier(name, ns, sep)
53
51
 
54
52
  path = name.to_s.gsub(sep, PATH_SEPARATOR)
55
53
  loader = options.fetch(:loader, Loader).new(path)
@@ -59,21 +57,14 @@ module Dry
59
57
  end
60
58
 
61
59
  # @api private
62
- def self.ensure_valid_namespace(ns, sep)
63
- ns_name = ns.to_s
64
- raise InvalidNamespaceError, ns_name if ns && ns_name.include?(sep)
65
- ns_name
66
- end
67
-
68
- # @api private
69
- def self.ensure_valid_identifier(name, ns_name, sep)
60
+ def self.ensure_valid_identifier(name, ns, sep)
70
61
  keys = name.to_s.scan(WORD_REGEX)
71
62
 
72
63
  if keys.uniq.size != keys.size
73
64
  raise InvalidComponentError, name, 'duplicated keys in the name'
74
65
  end
75
66
 
76
- keys.reject { |s| ns_name == s }.join(sep)
67
+ keys.reject { |s| ns.to_s.include?(s) }.join(sep)
77
68
  end
78
69
 
79
70
  # @api private
@@ -152,6 +143,11 @@ module Dry
152
143
  options[:namespace]
153
144
  end
154
145
 
146
+ # @api private
147
+ def auto_register?
148
+ !!options.fetch(:auto_register) { true }
149
+ end
150
+
155
151
  # @api private
156
152
  def root_key
157
153
  namespaces.first
@@ -1,6 +1,7 @@
1
1
  module Dry
2
2
  module System
3
3
  RB_EXT = '.rb'.freeze
4
+ RB_GLOB = '*.rb'.freeze
4
5
  EMPTY_STRING = ''.freeze
5
6
  PATH_SEPARATOR = '/'.freeze
6
7
  DEFAULT_SEPARATOR = '.'.freeze
@@ -8,6 +8,7 @@ require 'dry/system/errors'
8
8
  require 'dry/system/loader'
9
9
  require 'dry/system/booter'
10
10
  require 'dry/system/auto_registrar'
11
+ require 'dry/system/manual_registrar'
11
12
  require 'dry/system/importer'
12
13
  require 'dry/system/component'
13
14
  require 'dry/system/constants'
@@ -67,10 +68,12 @@ module Dry
67
68
  setting :default_namespace
68
69
  setting :root, Pathname.pwd.freeze
69
70
  setting :system_dir, 'system'.freeze
71
+ setting :registrations_dir, 'container'.freeze
70
72
  setting :auto_register, []
71
73
  setting :loader, Dry::System::Loader
72
74
  setting :booter, Dry::System::Booter
73
75
  setting :auto_registrar, Dry::System::AutoRegistrar
76
+ setting :manual_registrar, Dry::System::ManualRegistrar
74
77
  setting :importer, Dry::System::Importer
75
78
 
76
79
  class << self
@@ -101,7 +104,6 @@ module Dry
101
104
  # class Core < Dry::System::Container
102
105
  # configure do |config|
103
106
  # config.root = Pathname("/path/to/app")
104
- # config.name = :core
105
107
  # config.auto_register = %w(lib/apis lib/core)
106
108
  # end
107
109
  # end
@@ -112,14 +114,13 @@ module Dry
112
114
  # class MyApp < Dry::System::Container
113
115
  # configure do |config|
114
116
  # config.root = Pathname("/path/to/app")
115
- # config.name = :core
116
117
  # config.auto_register = %w(lib/apis lib/core)
117
118
  # end
118
119
  #
119
120
  # import core: Core
120
121
  # end
121
122
  #
122
- # @param other [Hash,Dry::Container::Namespace,Dry::System::Container]
123
+ # @param other [Hash,Dry::Container::Namespace]
123
124
  #
124
125
  # @api public
125
126
  def import(other)
@@ -127,9 +128,7 @@ module Dry
127
128
  when Hash then importer.register(other)
128
129
  when Dry::Container::Namespace then super
129
130
  else
130
- if other < System::Container
131
- importer.register(other.config.name => other)
132
- end
131
+ raise ArgumentError, "+other+ must be a hash of names and systems, or a Dry::Container namespace"
133
132
  end
134
133
  end
135
134
 
@@ -247,6 +246,7 @@ module Dry
247
246
 
248
247
  importer.finalize!
249
248
  booter.finalize!
249
+ manual_registrar.finalize!
250
250
  auto_registrar.finalize!
251
251
 
252
252
  freeze
@@ -312,11 +312,18 @@ module Dry
312
312
  self
313
313
  end
314
314
 
315
+ # @api public
316
+ def load_registrations!(name)
317
+ manual_registrar.(name)
318
+ self
319
+ end
320
+
315
321
  # Auto-registers components from the provided directory
316
322
  #
317
323
  # Typically you want to configure auto_register directories, and it will
318
324
  # work automatically. Use this method in cases where you want to have an
319
- # explicit way where some components are auto-registered.
325
+ # explicit way where some components are auto-registered, or if you want
326
+ # to exclude some components from been auto-registered
320
327
  #
321
328
  # @example
322
329
  # class MyApp < Dry::System::Container
@@ -328,15 +335,21 @@ module Dry
328
335
  # auto_register!('lib/core')
329
336
  #
330
337
  # # with a dir and a custom registration block
331
- # auto_register!('lib/core') do |component|
332
- # # custom way of initializing a component
338
+ # auto_register!('lib/core') do |config|
339
+ # config.instance do |component|
340
+ # # custom way of initializing a component
341
+ # end
342
+ #
343
+ # config.exclude do |component|
344
+ # # return true to exclude component from auto-registration
345
+ # end
333
346
  # end
334
347
  # end
335
348
  #
336
349
  # @param [String] dir The dir name relative to the root dir
337
350
  #
338
- # @yield [Component]
339
- # @see [Component]
351
+ # @yield AutoRegistrar::Configuration
352
+ # @see AutoRegistrar::Configuration
340
353
  #
341
354
  # @return [self]
342
355
  #
@@ -435,18 +448,24 @@ module Dry
435
448
  @auto_registrar ||= config.auto_registrar.new(self)
436
449
  end
437
450
 
451
+ # @api private
452
+ def manual_registrar
453
+ @manual_registrar ||= config.manual_registrar.new(self)
454
+ end
455
+
438
456
  # @api private
439
457
  def importer
440
458
  @importer ||= config.importer.new(self)
441
459
  end
442
460
 
443
461
  # @api private
444
- def component(key)
462
+ def component(key, **options)
445
463
  Component.new(
446
464
  key,
447
465
  loader: config.loader,
448
466
  namespace: config.default_namespace,
449
- separator: config.namespace_separator
467
+ separator: config.namespace_separator,
468
+ **options,
450
469
  )
451
470
  end
452
471
 
@@ -483,15 +502,17 @@ module Dry
483
502
  private
484
503
 
485
504
  # @api private
486
- def load_local_component(component, fallback = false)
505
+ def load_local_component(component, default_namespace_fallback = false)
487
506
  if component.bootable?(booter.path) || component.file_exists?(load_paths)
488
507
  booter.boot_dependency(component) unless frozen?
489
508
 
490
509
  require_component(component) do
491
510
  register(component.identifier) { component.instance }
492
511
  end
493
- elsif !fallback
512
+ elsif !default_namespace_fallback
494
513
  load_local_component(component.prepend(config.default_namespace), true)
514
+ elsif manual_registrar.file_exists?(component)
515
+ manual_registrar.(component)
495
516
  else
496
517
  raise ComponentLoadError, component
497
518
  end
@@ -10,21 +10,21 @@ module Dry
10
10
  end
11
11
  end
12
12
 
13
- # Error raised when a resolved component couldn't be found
13
+ # Error raised when booter file do not match with register component
14
14
  #
15
15
  # @api public
16
- ComponentLoadError = Class.new(StandardError) do
17
- def initialize(component)
18
- super("could not load component #{component.inspect}")
16
+ ComponentFileMismatchError = Class.new(StandardError) do
17
+ def initialize(filename, registered_booted_keys)
18
+ super("Mismatch between filename +#{filename}+ and registered components +#{registered_booted_keys}+")
19
19
  end
20
20
  end
21
21
 
22
- # Error raised when invalid namespace name was provided
22
+ # Error raised when a resolved component couldn't be found
23
23
  #
24
24
  # @api public
25
- InvalidNamespaceError = Class.new(StandardError) do
26
- def initialize(ns)
27
- super("Namespace #{ns} cannot include a separator")
25
+ ComponentLoadError = Class.new(StandardError) do
26
+ def initialize(component)
27
+ super("could not load component #{component.inspect}")
28
28
  end
29
29
  end
30
30
 
@@ -0,0 +1,31 @@
1
+ module Dry
2
+ module System
3
+ class MagicCommentsParser
4
+ VALID_LINE_RE = /^(#.*)?$/.freeze
5
+ COMMENT_RE = /^#\s+(?<name>[A-Za-z]{1}[A-Za-z0-9_]+):\s+(?<value>.+?)$/.freeze
6
+
7
+ COERCIONS = {
8
+ 'true' => true,
9
+ 'false' => false,
10
+ }.freeze
11
+
12
+ def self.call(file_name)
13
+ {}.tap do |options|
14
+ File.foreach(file_name) do |line|
15
+ break unless line =~ VALID_LINE_RE
16
+
17
+ if (comment = line.match(COMMENT_RE))
18
+ options[comment[:name].to_sym] = coerce(comment[:value])
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def self.coerce(value)
27
+ COERCIONS.fetch(value) { value }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,57 @@
1
+ require 'pathname'
2
+ require 'dry/system/constants'
3
+
4
+ module Dry
5
+ module System
6
+ # Default manual registration implementation
7
+ #
8
+ # This is currently configured by default for every System::Container.
9
+ # Manual registrar objects are responsible for loading files from configured
10
+ # manual registration paths, which should hold code to explicitly register
11
+ # certain objects with the container.
12
+ #
13
+ # @api private
14
+ class ManualRegistrar
15
+ attr_reader :container
16
+
17
+ attr_reader :config
18
+
19
+ def initialize(container)
20
+ @container = container
21
+ @config = container.config
22
+ end
23
+
24
+ # @api private
25
+ def finalize!
26
+ Dir[registrations_dir.join(RB_GLOB)].each do |file|
27
+ call(File.basename(file, RB_EXT))
28
+ end
29
+ end
30
+
31
+ # @api private
32
+ def call(name)
33
+ name = name.respond_to?(:root_key) ? name.root_key.to_s : name
34
+
35
+ require(root.join(config.registrations_dir, name))
36
+ end
37
+
38
+ def file_exists?(name)
39
+ name = name.respond_to?(:root_key) ? name.root_key.to_s : name
40
+
41
+ File.exist?(File.join(registrations_dir, "#{name}#{RB_EXT}"))
42
+ end
43
+
44
+ private
45
+
46
+ # @api private
47
+ def registrations_dir
48
+ root.join(config.registrations_dir)
49
+ end
50
+
51
+ # @api private
52
+ def root
53
+ Pathname(container.root)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  module System
3
- VERSION = '0.6.0'.freeze
3
+ VERSION = '0.7.0'.freeze
4
4
  end
5
5
  end
@@ -0,0 +1,4 @@
1
+ # auto_register: false
2
+
3
+ class NoRegister
4
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This is a file with a mix of valid and invalid magic comments
4
+
5
+ # valid_comment: hello
6
+ # true_comment: true
7
+ # false_comment: false
8
+ # comment_123: alpha-numeric and underscores allowed
9
+ # 123_will_not_match: will not match
10
+ # not-using-underscores: value for comment using dashes
11
+
12
+ # not_at_start_of_line: will not match
13
+
14
+ module Test
15
+ end
16
+
17
+ # after_code: will not match
@@ -0,0 +1,6 @@
1
+ Test::Container.namespace(:foo) do |container|
2
+ container.register('special') do
3
+ require 'test/foo'
4
+ Test::Foo.new('special')
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ module Test
2
+ class Foo
3
+ attr_reader :name
4
+
5
+ def initialize(name)
6
+ @name = name
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module Multiple
2
+ module Level
3
+ class Baz
4
+ include Test::Container.injector["foz"]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module Multiple
2
+ module Level
3
+ class Foz
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ Test::Container.finalize :heaven do |container|
2
+ container.register('heaven', 'string')
3
+ end
@@ -0,0 +1,3 @@
1
+ Test::Container.finalize :heaven do |container|
2
+ container.register('heaven', 'string')
3
+ end
@@ -0,0 +1,18 @@
1
+ RSpec.describe 'Lazy-loading manual registration files' do
2
+ before do
3
+ module Test
4
+ class Container < Dry::System::Container
5
+ configure do |config|
6
+ config.root = SPEC_ROOT.join('fixtures/manual_registration').realpath
7
+ end
8
+
9
+ load_paths!('lib')
10
+ end
11
+ end
12
+ end
13
+
14
+ it 'loads a manual registration file if the component could not be found' do
15
+ expect(Test::Container['foo.special']).to be_a(Test::Foo)
16
+ expect(Test::Container['foo.special'].name).to eq "special"
17
+ end
18
+ end
@@ -36,7 +36,7 @@ RSpec.describe 'Lazy-booting external deps' do
36
36
 
37
37
  before do
38
38
  module Test
39
- Umbrella.import(App)
39
+ Umbrella.import(main: App)
40
40
  Import = Umbrella.injector
41
41
  end
42
42
  end
@@ -53,7 +53,7 @@ RSpec.describe 'Lazy-booting external deps' do
53
53
 
54
54
  before do
55
55
  module Test
56
- App.import(Umbrella)
56
+ App.import(core: Umbrella)
57
57
  Import = App.injector
58
58
  end
59
59
  end
@@ -0,0 +1,26 @@
1
+ require 'dry/system/auto_registrar/configuration'
2
+
3
+ RSpec.describe Dry::System::AutoRegistrar::Configuration do
4
+ subject(:auto_registration_conf) { Dry::System::AutoRegistrar::Configuration.new }
5
+
6
+ describe "deafult values" do
7
+ it "will setup exclude default proc" do
8
+ expect(subject.exclude.(8)).to eq false
9
+ end
10
+
11
+ it "will setup instance default proc" do
12
+ component = double("component")
13
+ expect(component).to receive(:instance)
14
+ subject.instance.(component)
15
+ end
16
+ end
17
+
18
+ describe "add custom proc object to configuration" do
19
+ it "execute proc that was previously save" do
20
+ proc = Proc.new { |value| value + 1 }
21
+ subject.instance(&proc)
22
+ result = subject.instance.(5)
23
+ expect(result).to eq 6
24
+ end
25
+ end
26
+ end
@@ -12,11 +12,6 @@ RSpec.describe Dry::System::Component do
12
12
  expect(create.()).to be(create.())
13
13
  end
14
14
 
15
- it 'raises when namespace includes a separator' do
16
- expect { Dry::System::Component.new('foo.bar.baz', namespace: 'foo.bar') }
17
- .to raise_error(Dry::System::InvalidNamespaceError, /foo\.bar/)
18
- end
19
-
20
15
  it 'raises when identifier/path has duplicated keys' do
21
16
  expect { Dry::System::Component.new('foo.bar.foo', namespace: 'foo') }
22
17
  .to raise_error(Dry::System::InvalidComponentError, /foo\.bar\.foo/)
@@ -16,9 +16,13 @@ RSpec.describe Dry::System::Container, '.auto_register!' do
16
16
  it { expect(Test::Container['foo']).to be_an_instance_of(Foo) }
17
17
  it { expect(Test::Container['bar']).to be_an_instance_of(Bar) }
18
18
  it { expect(Test::Container['bar.baz']).to be_an_instance_of(Bar::Baz) }
19
+
20
+ it "doesn't register files with inline option 'auto_register: false'" do
21
+ expect(Test::Container.key?('no_register')).to eql false
22
+ end
19
23
  end
20
24
 
21
- context 'with custom registration block' do
25
+ context 'with custom configuration block' do
22
26
  before do
23
27
  class Test::Container < Dry::System::Container
24
28
  configure do |config|
@@ -29,14 +33,21 @@ RSpec.describe Dry::System::Container, '.auto_register!' do
29
33
  end
30
34
  end
31
35
 
32
- it 'yields found components' do
33
- Test::Container.auto_register!('components') do |component|
34
- component.identifier
36
+ it 'exclude specific components' do
37
+ Test::Container.auto_register!('components') do |config|
38
+ config.instance do |component|
39
+ component.identifier
40
+ end
41
+
42
+ config.exclude do |component|
43
+ component.path =~ /bar/
44
+ end
35
45
  end
36
46
 
37
47
  expect(Test::Container['foo']).to eql('foo')
38
- expect(Test::Container['bar']).to eql('bar')
39
- expect(Test::Container['bar.baz']).to eql('bar.baz')
48
+
49
+ expect(Test::Container.key?('bar')).to eql false
50
+ expect(Test::Container.key?('bar.baz')).to eql false
40
51
  end
41
52
  end
42
53
 
@@ -58,6 +69,23 @@ RSpec.describe Dry::System::Container, '.auto_register!' do
58
69
  specify { expect(Test::Container['foo']).to be_a(Namespaced::Foo) }
59
70
  end
60
71
 
72
+ context 'standard loader with a default namespace with multiple level' do
73
+ before do
74
+ class Test::Container < Dry::System::Container
75
+ configure do |config|
76
+ config.root = SPEC_ROOT.join('fixtures').realpath
77
+ config.default_namespace = 'multiple.level'
78
+ end
79
+
80
+ load_paths!('multiple_namespaced_components')
81
+ auto_register!('multiple_namespaced_components')
82
+ end
83
+ end
84
+
85
+ specify { expect(Test::Container['baz']).to be_a(Multiple::Level::Baz) }
86
+ specify { expect(Test::Container['foz']).to be_a(Multiple::Level::Foz) }
87
+ end
88
+
61
89
  context 'with a custom loader' do
62
90
  before do
63
91
  class Test::Loader < Dry::System::Loader
@@ -9,31 +9,14 @@ RSpec.describe Dry::System::Container, '.import' do
9
9
  end
10
10
  end
11
11
 
12
- shared_examples_for 'an extended container' do
13
- it 'imports one container into another' do
14
- expect(app.key?('persistence.users')).to be(false)
12
+ it 'imports one container into another' do
13
+ app.import(persistence: db)
15
14
 
16
- app.finalize!
15
+ expect(app.key?('persistence.users')).to be(false)
17
16
 
18
- expect(app['persistence.users']).to eql(%w(jane joe))
19
- end
20
- end
21
-
22
- context 'when a container has a name' do
23
- before do
24
- db.configure { |c| c.name = :persistence }
25
- app.import(db)
26
- end
27
-
28
- it_behaves_like 'an extended container'
29
- end
30
-
31
- context 'when container does not have a name' do
32
- before do
33
- app.import(persistence: db)
34
- end
17
+ app.finalize!
35
18
 
36
- it_behaves_like 'an extended container'
19
+ expect(app['persistence.users']).to eql(%w(jane joe))
37
20
  end
38
21
 
39
22
  describe 'import module' do
@@ -142,6 +142,14 @@ RSpec.describe Dry::System::Container do
142
142
  'component identifier +foo+ is invalid or boot file is missing'
143
143
  )
144
144
  end
145
+
146
+ describe "missmatch betwenn finalize name and registered component" do
147
+ it "raises a meaningful error" do
148
+ expect{
149
+ container.boot!(:hell)
150
+ }.to raise_error(Dry::System::ComponentFileMismatchError)
151
+ end
152
+ end
145
153
  end
146
154
 
147
155
  context 'with the default core dir' do
@@ -0,0 +1,41 @@
1
+ require 'dry/system/magic_comments_parser'
2
+
3
+ RSpec.describe Dry::System::MagicCommentsParser, '.call' do
4
+ let(:file_name) { SPEC_ROOT.join('fixtures/magic_comments/comments.rb') }
5
+ let(:comments) { described_class.(file_name) }
6
+
7
+ it 'makes comment names available as symbols' do
8
+ expect(comments.key?(:valid_comment)).to eql true
9
+ end
10
+
11
+ it 'finds magic comments after other commented lines or blank lines' do
12
+ expect(comments[:valid_comment]).to eq 'hello'
13
+ end
14
+
15
+ it 'does not match comments with invalid names' do
16
+ expect(comments.values).not_to include 'value for comment using dashes'
17
+ end
18
+
19
+ it 'supports comment names with alpha-numeric characters and underscores (numbers not allowed for first character)' do
20
+ expect(comments[:comment_123]).to eq 'alpha-numeric and underscores allowed'
21
+ expect(comments.keys).not_to include(:"123_will_not_match")
22
+ end
23
+
24
+ it 'only matches comments at the start of the line' do
25
+ expect(comments.key?(:not_at_start_of_line)).to eql false
26
+ end
27
+
28
+ it 'does not match any comments after any lines of code' do
29
+ expect(comments.key?(:after_code)).to eql false
30
+ end
31
+
32
+ describe 'coercions' do
33
+ it 'coerces "true" to true' do
34
+ expect(comments[:true_comment]).to eq true
35
+ end
36
+
37
+ it 'coerces "false" to false' do
38
+ expect(comments[:false_comment]).to eq false
39
+ end
40
+ end
41
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-system
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-02 00:00:00.000000000 Z
11
+ date: 2017-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inflecto
@@ -145,16 +145,22 @@ extra_rdoc_files: []
145
145
  files:
146
146
  - ".gitignore"
147
147
  - ".rspec"
148
- - ".rubocop.yml"
149
- - ".rubocop_todo.yml"
150
148
  - ".travis.yml"
151
149
  - ".yardopts"
152
150
  - CHANGELOG.md
151
+ - CONTRIBUTING.md
153
152
  - Gemfile
154
153
  - LICENSE
155
154
  - README.md
156
155
  - Rakefile
157
156
  - dry-system.gemspec
157
+ - examples/custom_configuration_auto_register/Gemfile
158
+ - examples/custom_configuration_auto_register/lib/entities/user.rb
159
+ - examples/custom_configuration_auto_register/lib/user_repo.rb
160
+ - examples/custom_configuration_auto_register/run.rb
161
+ - examples/custom_configuration_auto_register/system/boot/persistence.rb
162
+ - examples/custom_configuration_auto_register/system/container.rb
163
+ - examples/custom_configuration_auto_register/system/import.rb
158
164
  - examples/standalone/Gemfile
159
165
  - examples/standalone/lib/user_repo.rb
160
166
  - examples/standalone/run.rb
@@ -164,6 +170,7 @@ files:
164
170
  - lib/dry-system.rb
165
171
  - lib/dry/system.rb
166
172
  - lib/dry/system/auto_registrar.rb
173
+ - lib/dry/system/auto_registrar/configuration.rb
167
174
  - lib/dry/system/booter.rb
168
175
  - lib/dry/system/component.rb
169
176
  - lib/dry/system/constants.rb
@@ -172,10 +179,13 @@ files:
172
179
  - lib/dry/system/importer.rb
173
180
  - lib/dry/system/lifecycle.rb
174
181
  - lib/dry/system/loader.rb
182
+ - lib/dry/system/magic_comments_parser.rb
183
+ - lib/dry/system/manual_registrar.rb
175
184
  - lib/dry/system/version.rb
176
185
  - spec/fixtures/components/bar.rb
177
186
  - spec/fixtures/components/bar/baz.rb
178
187
  - spec/fixtures/components/foo.rb
188
+ - spec/fixtures/components/no_register.rb
179
189
  - spec/fixtures/import_test/config/application.yml
180
190
  - spec/fixtures/import_test/lib/test/bar.rb
181
191
  - spec/fixtures/import_test/lib/test/foo.rb
@@ -187,9 +197,15 @@ files:
187
197
  - spec/fixtures/lazytest/lib/test/models/book.rb
188
198
  - spec/fixtures/lazytest/lib/test/models/user.rb
189
199
  - spec/fixtures/lazytest/system/boot/bar.rb
200
+ - spec/fixtures/magic_comments/comments.rb
201
+ - spec/fixtures/manual_registration/container/foo.rb
202
+ - spec/fixtures/manual_registration/lib/test/foo.rb
203
+ - spec/fixtures/multiple_namespaced_components/multiple/level/baz.rb
204
+ - spec/fixtures/multiple_namespaced_components/multiple/level/foz.rb
190
205
  - spec/fixtures/namespaced_components/namespaced/bar.rb
191
206
  - spec/fixtures/namespaced_components/namespaced/foo.rb
192
207
  - spec/fixtures/other/config/boot/bar.rb
208
+ - spec/fixtures/other/config/boot/hell.rb
193
209
  - spec/fixtures/other/lib/test/dep.rb
194
210
  - spec/fixtures/other/lib/test/foo.rb
195
211
  - spec/fixtures/other/lib/test/models.rb
@@ -207,11 +223,14 @@ files:
207
223
  - spec/fixtures/test/system/boot/bar.rb
208
224
  - spec/fixtures/test/system/boot/client.rb
209
225
  - spec/fixtures/test/system/boot/db.rb
226
+ - spec/fixtures/test/system/boot/hell.rb
210
227
  - spec/fixtures/test/system/boot/logger.rb
211
228
  - spec/fixtures/umbrella/system/boot/db.rb
212
229
  - spec/integration/boot_spec.rb
230
+ - spec/integration/container/lazy_loading/manual_registration_spec.rb
213
231
  - spec/integration/import_spec.rb
214
232
  - spec/spec_helper.rb
233
+ - spec/unit/auto_registrar/configuration_spec.rb
215
234
  - spec/unit/component_spec.rb
216
235
  - spec/unit/container/auto_register_spec.rb
217
236
  - spec/unit/container/finalize_spec.rb
@@ -219,6 +238,7 @@ files:
219
238
  - spec/unit/container/injector_spec.rb
220
239
  - spec/unit/container_spec.rb
221
240
  - spec/unit/loader_spec.rb
241
+ - spec/unit/magic_comments_parser_spec.rb
222
242
  homepage: http://dry-rb.org/gems/dry-system
223
243
  licenses:
224
244
  - MIT
@@ -239,7 +259,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
239
259
  version: '0'
240
260
  requirements: []
241
261
  rubyforge_project:
242
- rubygems_version: 2.6.9
262
+ rubygems_version: 2.6.11
243
263
  signing_key:
244
264
  specification_version: 4
245
265
  summary: Organize your code into reusable components
@@ -247,6 +267,7 @@ test_files:
247
267
  - spec/fixtures/components/bar.rb
248
268
  - spec/fixtures/components/bar/baz.rb
249
269
  - spec/fixtures/components/foo.rb
270
+ - spec/fixtures/components/no_register.rb
250
271
  - spec/fixtures/import_test/config/application.yml
251
272
  - spec/fixtures/import_test/lib/test/bar.rb
252
273
  - spec/fixtures/import_test/lib/test/foo.rb
@@ -258,9 +279,15 @@ test_files:
258
279
  - spec/fixtures/lazytest/lib/test/models/book.rb
259
280
  - spec/fixtures/lazytest/lib/test/models/user.rb
260
281
  - spec/fixtures/lazytest/system/boot/bar.rb
282
+ - spec/fixtures/magic_comments/comments.rb
283
+ - spec/fixtures/manual_registration/container/foo.rb
284
+ - spec/fixtures/manual_registration/lib/test/foo.rb
285
+ - spec/fixtures/multiple_namespaced_components/multiple/level/baz.rb
286
+ - spec/fixtures/multiple_namespaced_components/multiple/level/foz.rb
261
287
  - spec/fixtures/namespaced_components/namespaced/bar.rb
262
288
  - spec/fixtures/namespaced_components/namespaced/foo.rb
263
289
  - spec/fixtures/other/config/boot/bar.rb
290
+ - spec/fixtures/other/config/boot/hell.rb
264
291
  - spec/fixtures/other/lib/test/dep.rb
265
292
  - spec/fixtures/other/lib/test/foo.rb
266
293
  - spec/fixtures/other/lib/test/models.rb
@@ -278,11 +305,14 @@ test_files:
278
305
  - spec/fixtures/test/system/boot/bar.rb
279
306
  - spec/fixtures/test/system/boot/client.rb
280
307
  - spec/fixtures/test/system/boot/db.rb
308
+ - spec/fixtures/test/system/boot/hell.rb
281
309
  - spec/fixtures/test/system/boot/logger.rb
282
310
  - spec/fixtures/umbrella/system/boot/db.rb
283
311
  - spec/integration/boot_spec.rb
312
+ - spec/integration/container/lazy_loading/manual_registration_spec.rb
284
313
  - spec/integration/import_spec.rb
285
314
  - spec/spec_helper.rb
315
+ - spec/unit/auto_registrar/configuration_spec.rb
286
316
  - spec/unit/component_spec.rb
287
317
  - spec/unit/container/auto_register_spec.rb
288
318
  - spec/unit/container/finalize_spec.rb
@@ -290,3 +320,4 @@ test_files:
290
320
  - spec/unit/container/injector_spec.rb
291
321
  - spec/unit/container_spec.rb
292
322
  - spec/unit/loader_spec.rb
323
+ - spec/unit/magic_comments_parser_spec.rb
data/.rubocop.yml DELETED
@@ -1,34 +0,0 @@
1
- # Generated by `rubocop --auto-gen-config`
2
- inherit_from: .rubocop_todo.yml
3
-
4
- # No need to handle LoadError in spec helper
5
- Lint/HandleExceptions:
6
- Exclude:
7
- - spec/spec_helper.rb
8
-
9
- Style/FileName:
10
- Exclude:
11
- - 'lib/dry-component.rb'
12
-
13
- # Documentation checked by Inch CI
14
- Style/Documentation:
15
- Enabled: false
16
-
17
- # Ain't nobody got time for that!
18
- Style/MethodCallParentheses:
19
- Enabled: false
20
-
21
- Style/Lambda:
22
- Enabled: false
23
-
24
- Style/BlockDelimiters:
25
- Enabled: false
26
-
27
- Style/LambdaCall:
28
- EnforcedStyle: braces
29
-
30
- Style/MultilineBlockChain:
31
- Enabled: false
32
-
33
- Style/StabbyLambdaParentheses:
34
- EnforcedStyle: require_no_parentheses
data/.rubocop_todo.yml DELETED
@@ -1,26 +0,0 @@
1
- # This configuration was generated by
2
- # `rubocop --auto-gen-config`
3
- # on 2015-12-23 21:42:26 +0000 using RuboCop version 0.35.1.
4
- # The point is for the user to remove these configuration records
5
- # one by one as the offenses are removed from the code base.
6
- # Note that changes in the inspected code, or installation of new
7
- # versions of RuboCop, may require this file to be generated again.
8
-
9
- # Offense count: 1
10
- Metrics/AbcSize:
11
- Max: 25
12
-
13
- # Offense count: 1
14
- # Configuration parameters: CountComments.
15
- Metrics/ClassLength:
16
- Max: 130
17
-
18
- # Offense count: 1
19
- # Configuration parameters: AllowURI, URISchemes.
20
- Metrics/LineLength:
21
- Max: 84
22
-
23
- # Offense count: 2
24
- # Configuration parameters: CountComments.
25
- Metrics/MethodLength:
26
- Max: 14