automatic_namespaces 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4127c0936ad000fe4b361c619dbd3d1b78a43d975d3d048432d93fd6d0f9f597
4
+ data.tar.gz: 6ad52407e24da1ab14e9264d16c7fd388db5a5eba94935ae8964689ed0280396
5
+ SHA512:
6
+ metadata.gz: 274f7b77c5340a6be552e7ba39a877336b04c63ff7126e472b2ca5244ca547e4d6ccb622606e44caebf49a707457da87efe0207870febb3f539111b4a716d71e
7
+ data.tar.gz: 2309e8898758ccc3d31157b55d035f4091e55b25693a58f72af57b53eba41712801bd07f1e27c727bbcd98083e68e604954cebc628e4857578b0927b3f7473bb
data/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # AutomaticNamespaces
2
+
3
+ This gem eases some pain related to strong namespaces in a modular monolith.
4
+
5
+ By default, Rails 7 autoloader (Zeitwerk) requires a separate "namespace" directory above a namespaced class. For example:
6
+
7
+ ```
8
+ app
9
+ ├── models
10
+ │ ├── component1
11
+ │ │ ├── class1.rb # contains Component1::Class1
12
+ ```
13
+
14
+ When building a modular monolith using packages ([packwerk](https://github.com/Shopify/packwerk) + [stimpack](https://github.com/rubyatscale/stimpack)),
15
+ this pattern creates a lot of extra noise in the directory structure:
16
+
17
+ ```
18
+ packs
19
+ ├── component1
20
+ │ ├── app
21
+ │ │ ├── controllers
22
+ │ │ │ ├── component1
23
+ │ │ │ │ ├── model1_controller.rb # contains Component1::Model1Controller
24
+ │ │ │ │ ├── model2_controller.rb # contains Component1::Model2Controller
25
+ │ │ ├── models
26
+ │ │ │ ├── component1
27
+ │ │ │ │ ├── model1.rb # contains Component1::Model1
28
+ │ │ │ │ ├── model2.rb # contains Component1::Model2
29
+ │ │ ├── public
30
+ │ │ │ ├── component1
31
+ │ │ │ │ ├── public_type1.rb # contains Component1::PublicType1
32
+ │ │ │ │ ├── public_type2.rb # contains Component1::PublicType2
33
+ │ │ ├── services
34
+ │ │ │ ├── component1
35
+ │ │ │ │ ├── service1.rb # contains Component1::Service1
36
+ │ │ │ │ ├── service2.rb # contains Component1::Service2
37
+ ```
38
+
39
+ And that's only for a single pack! As your modular monolith grows, you'll likely have dozens (maybe you'll have
40
+ hundreds) of packs. That's a lot of "namespace" directories that aren't adding a lot of value. You already
41
+ know the namespace of those classes in a strongly namespaced pack -- it's the pack name -- can Zeitwerk know it, too?
42
+
43
+ This gem patches the Rails 7 autoloader so that most subdirectories under your strongly namespaced component's `app` directory are
44
+ automatically associated with the namespace.
45
+
46
+ ## Installation
47
+
48
+ Install the gem and add to the application's Gemfile by executing:
49
+
50
+ $ bundle add automatic_namespaces
51
+
52
+ If bundler is not being used to manage dependencies, install the gem by executing:
53
+
54
+ $ gem install automatic_namespaces
55
+
56
+ ## Usage
57
+
58
+ Given the `package.yml` of a strongly namespaced pack:
59
+
60
+ ```
61
+ enforce_dependencies: true
62
+ enforce_privacy: true
63
+ public_path: app/public/
64
+ dependencies:
65
+ - "packs/components/core_ui"
66
+ - "packs/components/core_ext"
67
+ - "packs/components/us_states"
68
+ - "packs/subsystems/products"
69
+ - "packs/subsystems/inventory"
70
+ metadata:
71
+ ```
72
+
73
+ modify the metadata to opt into automatic namespacing:
74
+
75
+ ```
76
+ metadata:
77
+ automatic_pack_namespace: true
78
+ ```
79
+
80
+ Now restructure your files to remove the extra namespace directories under `app`. Note that some directories do not
81
+ generally contain namespaced classes. These are exempted from `automatic_namespaces`:
82
+
83
+ * assets
84
+ * helpers
85
+ * javascript
86
+ * views
87
+
88
+ If your package / namespace name requires ActiveSupport inflections, you will probably need to tell `automatic_namespaces`
89
+ what the correct namespace name should be in that package:
90
+
91
+ ```
92
+ # packs/shoes_ui/package.yml
93
+ metadata:
94
+ automatic_pack_namespace: true
95
+ namespace_override: ShoesUI
96
+ ```
97
+
98
+ This is necessary because `automatic_namespaces` works by modifying the autoloader paths, which has to
99
+ happen during Rails application initialization; but the inflector is not available for use then.
100
+
101
+ ## Development
102
+
103
+ After checking out the repo, run `bundle instal` to install dependencies. Then, run `rspec` to run the tests.
104
+
105
+ To 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).
106
+
107
+ ## Credits
108
+
109
+ Thanks to a handful of individuals who directly contributed to the creation of this gem:
110
+
111
+ * [Caleb Woods](https://github.com/alexevanczuk) - For feeling the pain and helping to spike an approach during a project kickoff at [RoleModel Software](www.rolemodelsoftware.com)
112
+ * [Alex Evanczuk](https://github.com/alexevanczuk) - For being willing to collaborate on the project, helping to refine the approach, and even mentoring me on how to create my first gem for sharing with others.
113
+ * [Xavier Noria](https://github.com/fxn) - For his suggestions on how to patch Zeitwerk to make this work at all, and for suggestions on how to keep hot reloading working after Rails autopaths were missing all pack directories.
114
+
115
+ ## Contributing
116
+
117
+ Bug reports and pull requests are welcome on GitHub at https://github.com/gap777/automatic_namespaces. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/gap777/automatic_namespaces/blob/main/CODE_OF_CONDUCT.md).
118
+
119
+ ## License
120
+
121
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
122
+
123
+ ## Code of Conduct
124
+
125
+ Everyone interacting in the AutomaticNamespaces project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/gap777/automatic_namespaces/blob/main/CODE_OF_CONDUCT.md).
@@ -0,0 +1,56 @@
1
+ require 'yaml'
2
+
3
+ class AutomaticNamespaces::Autoloader
4
+
5
+ def enable_automatic_namespaces
6
+ namespaced_packages.each do |pack, metadata|
7
+ package_namespace = define_namespace(pack, metadata)
8
+ pack_directories(pack.path).each do |pack_dir|
9
+ set_namespace_for(pack_dir, package_namespace)
10
+ end
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def set_namespace_for(pack_dir, package_namespace)
17
+ Rails.logger.debug { "Associating #{pack_dir} with namespace #{package_namespace}" }
18
+ ActiveSupport::Dependencies.autoload_paths.delete(pack_dir)
19
+ Rails.autoloaders.main.push_dir(pack_dir, namespace: package_namespace)
20
+ Rails.application.config.watchable_dirs[pack_dir] = [:rb]
21
+ end
22
+
23
+ def pack_directories(pack_root_dir)
24
+ Dir.glob("#{pack_root_dir}/app/*").reject { |dir| non_namspaced_directory(dir) }
25
+ end
26
+
27
+ def non_namspaced_directory(dir)
28
+ dir.include?('/app/assets') ||
29
+ dir.include?('/app/helpers') || # Rails assumes helpers are global, not namespaced
30
+ dir.include?('/app/inputs') || # Not sure how to namespace form inputs
31
+ dir.include?('/app/javascript') ||
32
+ dir.include?('/app/views')
33
+ end
34
+
35
+ def define_namespace(pack, metadata)
36
+ namespace = metadata['namespace_override'] || pack.name.camelize
37
+ Object.const_set(namespace, Module.new)
38
+ namespace.constantize
39
+ end
40
+
41
+ def namespaced_packages
42
+ Stimpack::Packs.all
43
+ .map {|pack| [pack, package_metadata(pack)] }
44
+ .select {|pack, metadata| metadata && metadata["automatic_pack_namespace"] }
45
+ end
46
+
47
+ def package_metadata(pack)
48
+ package_file = pack.path.join('package.yml').to_s
49
+ package_description = YAML.load_file(package_file) || {}
50
+ package_description["metadata"]
51
+ end
52
+ end
53
+
54
+
55
+
56
+
@@ -0,0 +1,9 @@
1
+ require "rails/railtie"
2
+
3
+ module AutomaticNamespaces
4
+ class Railtie < Rails::Railtie
5
+ initializer 'automatic_namespaces' do
6
+ Autoloader.new.enable_automatic_namespaces
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AutomaticNamespaces
4
+ VERSION = "0.1.1"
5
+ end
@@ -0,0 +1,17 @@
1
+
2
+ require "active_support"
3
+ require_relative "automatic_namespaces/version"
4
+ require_relative "automatic_namespaces/autoloader"
5
+
6
+ module AutomaticNamespaces
7
+
8
+ class Error < StandardError; end
9
+ extend ActiveSupport::Autoload
10
+
11
+ autoload :Autoloader
12
+ autoload :Railtie
13
+
14
+ private_constant :Autoloader
15
+ end
16
+
17
+ require "automatic_namespaces/railtie"
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: automatic_namespaces
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Gary Passero
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-11-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: stimpack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: debug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
98
+ email:
99
+ - gpassero@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - README.md
105
+ - lib/automatic_namespaces.rb
106
+ - lib/automatic_namespaces/autoloader.rb
107
+ - lib/automatic_namespaces/railtie.rb
108
+ - lib/automatic_namespaces/version.rb
109
+ homepage: https://github.com/gap777/automatic_namespaces
110
+ licenses:
111
+ - MIT
112
+ metadata:
113
+ homepage_uri: https://github.com/gap777/automatic_namespaces
114
+ source_code_uri: https://github.com/alexevanczuk/my_example_gem
115
+ changelog_uri: https://github.com/alexevanczuk/my_example_gem/releases
116
+ allowed_push_host: https://rubygems.org
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: 2.6.0
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubygems_version: 3.1.6
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Modify autoloading to assume all files within a directory belong in a namespace
136
+ test_files: []