automatic_namespaces 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +125 -0
- data/lib/automatic_namespaces/autoloader.rb +56 -0
- data/lib/automatic_namespaces/railtie.rb +9 -0
- data/lib/automatic_namespaces/version.rb +5 -0
- data/lib/automatic_namespaces.rb +17 -0
- metadata +136 -0
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,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: []
|