packs-rails 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cd98f383feaab97c239cf1281a65c3fe8c4ab9ca623d69f7138fc8bcc1729092
4
+ data.tar.gz: d8042586ae2abd53b01fced0e59069d770728272f853e722c27b4bf76e2a9efb
5
+ SHA512:
6
+ metadata.gz: 9ac6362a82c7cdc78c22683176c3e3b319da85824f1c3013a60fd03816082af280fce9fb605da07431184f81916a982be6ee43f246b516bd195f29d00b811145
7
+ data.tar.gz: 8b0699e9bed0a6923bf74b70fa614551b0ec446dc3492ec9bdb6fdd5e1ad94b0a2bb5934f4bb806858a97e4017855555adff9c7c84234b748272c8716ec0d182
data/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # packs-rails
2
+
3
+ `packs-rails` establishes and implements a set of conventions for splitting up large monoliths built on top of the [`packs`](https://github.com/rubyatscale/packs) standard. `packs-rails` makes it easy to use [`packwerk`](https://github.com/Shopify/packwerk) to modularize your rails app. With `packs-rails`, new packages' autoload paths are automatically added to Rails, so your code will immediately become usable and loadable without additional configuration.
4
+
5
+ Here is an example application that uses `packs-rails`:
6
+ ```
7
+ package.yml # root level pack
8
+ app/ # Unpackaged code
9
+ models/
10
+ ...
11
+ packs/
12
+ my_domain/
13
+ package.yml # See the packwerk docs for more info
14
+ deprecated_references.yml # See the packwerk docs for more info
15
+ app/
16
+ public/ # Recommended location for public API
17
+ my_domain.rb # creates the primary namespaces
18
+ my_domain/
19
+ my_subdomain.rb
20
+ services/ # Private services
21
+ my_domain/
22
+ some_private_class.rb
23
+ models/ # Private models
24
+ some_other_non_namespaced_private_model.rb # this works too
25
+ my_domain/
26
+ my_private_namespacd_model.rb
27
+ controllers/
28
+ views/
29
+ config/
30
+ initializers/ # Initializers can live in packs and load as expected
31
+ lib/
32
+ tasks/
33
+ spec/ # With packs-rails, specs for a pack live next to the pack
34
+ public/
35
+ my_domain_spec.rb
36
+ my_domain/
37
+ my_subdomain_spec.rb
38
+ services/
39
+ my_domain/
40
+ some_private_class_spec.rb
41
+ models/
42
+ some_other_non_namespaced_private_model_spec.rb
43
+ my_domain/
44
+ my_private_namespaced_model_spec.rb
45
+ factories/ # packs-rails will automatically load pack factories into FactoryBot
46
+ my_domain/
47
+ my_private_namespaced_model_factory.rb
48
+ my_other_domain/
49
+ ... # other packs have a similar structure
50
+ my_other_other_domain/
51
+ ...
52
+ ```
53
+
54
+ ## Usage
55
+
56
+ Setting up `packs-rails` is straightforward. Simply by including `packs-rails` in your `Gemfile` in all environments, `packs-rails` will automatically hook into and configure Rails.
57
+
58
+ From there, you can create a `./packs` folder and structure it using the conventions listed above.
59
+
60
+ If you wish to use a different directory name, eg `components` instead of `packs`, you can customize this by configuring `packs.yml`. See [`packs`](https://github.com/rubyatscale/packs) for more information.
61
+
62
+ ### Splitting routes
63
+ `packs-rails` allows you to split your application routes for every pack. You just have to create a file describing your routes and then `draw` them in your root `config/routes.rb` file.
64
+
65
+ ```ruby
66
+ # packs/my_domain/config/routes/my_domain.rb
67
+ resources :my_resource
68
+
69
+ # config/routes.rb
70
+ draw(:my_domain)
71
+ ```
72
+
73
+ ### Making your Package an Engine
74
+ Add `engine: true` to your `package.yml` to make it an actual Rails engine ([read more about what this means here](https://guides.rubyonrails.org/engines.html)):
75
+ ```yml
76
+ # packs/my_pack/package.yml
77
+ enforce_dependencies: true
78
+ enforce_privacy: true
79
+ metadata:
80
+ engine: true
81
+ ```
82
+
83
+ ## Ecosystem and Integrations
84
+
85
+ ### RSpec Integration
86
+ Simply add `--require packs/rails/rspec` to your `.rspec`.
87
+ Or, if you'd like, pass it as an argument to `rspec`:
88
+
89
+ ```
90
+ $ rspec --require packs/rails/rspec ...
91
+ ```
92
+
93
+ Integration will allow you to run tests as such:
94
+ ```
95
+ # Run all specs in your entire application (packs and all):
96
+ rspec
97
+
98
+ # Run just that one test:
99
+ rspec spec/some/specific_spec.rb
100
+
101
+ # Run all tests under the "foobar" pack and all the tests of its nested packs:
102
+ rspec packs/foobar
103
+
104
+ # Same as above but also adds the "binbaz" pack:
105
+ rspec packs/foobar pack/binbaz
106
+
107
+ # Run all test files inside the "packs/foobar/spec" directory:
108
+ rspec packs/foobar/spec
109
+
110
+ # Run all specs under the "packs/foobar/nested_pack" pack:
111
+ rspec packs/foobar/nested_pack
112
+ ```
113
+
114
+ #### parallel_tests
115
+
116
+ `parallel_tests` has it its own spec discovery mechanism, so packs-rails's RSpec integration doesn't do anything when you use them together.
117
+ To make them work, you'll need to explicitly specify the spec paths:
118
+
119
+ ```bash
120
+ RAILS_ENV=test bundle exec parallel_test spec packs/**/spec -t rspec <other_options>
121
+ ```
122
+
123
+ #### Knapsack (Pro)
124
+
125
+ Similarly, Knapsack (and its Pro version) has its own spec discovery mechanism and the API will find and queue the relevant specs.
126
+ To make it discover your pack tests as well, you'll need to configure the following variables:
127
+
128
+ ```yaml
129
+ KNAPSACK_PRO_TEST_DIR: spec
130
+ KNAPSACK_PRO_TEST_FILE_PATTERN: '{spec,packs}/**{,/*/**}/*_spec.rb'
131
+ ```
132
+
133
+ Setting `KNAPSACK_PRO_TEST_FILE_PATTERN` will tell Knapsack where your specs are located, while setting `KNAPSACK_PRO_TEST_DIR` is necessary because otherwise an incorrect value is sent to rspec via the `--default-path` option.
134
+
135
+ ## Contributing
136
+
137
+ Bug reports and pull requests are welcome on GitHub at https://github.com/rubyatscale/packs-rails.
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "packs-rails"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/bin/tapioca ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'tapioca' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("tapioca", "tapioca")
@@ -0,0 +1,17 @@
1
+ # typed: true
2
+
3
+ module Packs
4
+ module Rails
5
+ module Integrations
6
+ class FactoryBot
7
+ def initialize(app)
8
+ return unless app.config.respond_to?(:factory_bot)
9
+
10
+ Packs.all.reject(&:is_gem?).each do |pack|
11
+ app.config.factory_bot.definition_file_paths << pack.relative_path.join("spec/factories").to_s
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require "active_support/inflections"
5
+
6
+ module Packs
7
+ module Rails
8
+ module Integrations
9
+ class Rails
10
+ def initialize(app)
11
+ @app = app
12
+
13
+ Packs::Rails.config.paths.freeze
14
+
15
+ create_engines
16
+ inject_paths
17
+ end
18
+
19
+ def create_engines
20
+ Packs.all.reject(&:is_gem?).each do |pack|
21
+ next unless pack.metadata['engine']
22
+
23
+ create_engine(pack)
24
+ end
25
+ end
26
+
27
+ def inject_paths
28
+ Packs.all.reject(&:is_gem?).each do |pack|
29
+ Packs::Rails.config.paths.each do |path|
30
+ @app.paths[path] << pack.relative_path.join(path)
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def create_namespace(name)
38
+ namespace = ActiveSupport::Inflector.camelize(name)
39
+ namespace.split("::").reduce(Object) do |base, mod|
40
+ if base.const_defined?(mod, false)
41
+ base.const_get(mod, false)
42
+ else
43
+ base.const_set(mod, Module.new)
44
+ end
45
+ end
46
+ end
47
+
48
+ def create_engine(pack)
49
+ name = pack.last_name
50
+ namespace = create_namespace(name)
51
+ stim = Stim.new(pack, namespace)
52
+ namespace.const_set("Engine", Class.new(::Rails::Engine)).include(stim)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,60 @@
1
+ # typed: true
2
+
3
+ module Packs
4
+ module Rails
5
+ module Integrations
6
+ class RSpec
7
+ extend T::Sig
8
+
9
+ def initialize
10
+ # This is the list of directories RSpec was told to run.
11
+ to_run = ::RSpec.configuration.instance_variable_get(:@files_or_directories_to_run)
12
+ default_path = ::RSpec.configuration.default_path
13
+
14
+ if to_run == [default_path]
15
+ # This is the default case when you run `rspec`. We want to add all the pack's spec paths
16
+ # to the collection of directories to run.
17
+
18
+ pack_paths = Packs.all.map do |pack|
19
+ spec_path = pack.relative_path.join(default_path)
20
+ spec_path.to_s if spec_path.exist?
21
+ end
22
+
23
+ to_run.concat(pack_paths)
24
+ else
25
+ # This is when `rspec` is run with a list of directories or files. We scan this list to see
26
+ # if any of them matches a pack's directory. If it does, we concat the `default_path` to the
27
+ # end of it.
28
+ #
29
+ # packs/my_pack => packs/my_pack/spec
30
+ #
31
+ # If it doesn't match a pack path, we leave it alone.
32
+
33
+ to_run.map! do |path|
34
+ if pack = Packs.find(path)
35
+ [
36
+ pack,
37
+ *nested_packs_for(pack)
38
+ ].map do |pack|
39
+ spec_path = pack.relative_path.join(default_path)
40
+ spec_path.to_s if spec_path.exist?
41
+ end
42
+ else
43
+ path
44
+ end
45
+ end
46
+ end
47
+
48
+ ::RSpec.configuration.files_or_directories_to_run = to_run.flatten.compact.uniq
49
+ end
50
+
51
+ sig { params(parent_pack: Packs::Pack).returns(T::Array[Packs::Pack]) }
52
+ def nested_packs_for(parent_pack)
53
+ Packs.all.select do |pack|
54
+ pack.name != parent_pack.name && pack.name.include?(parent_pack.name)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,11 @@
1
+ require "active_support"
2
+
3
+ module Packs
4
+ module Rails
5
+ module Integrations
6
+ autoload :FactoryBot, "packs/rails/integrations/factory_bot"
7
+ autoload :Rails, "packs/rails/integrations/rails"
8
+ autoload :RSpec, "packs/rails/integrations/rspec"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ require "rails/railtie"
2
+
3
+ module Packs
4
+ module Rails
5
+ class Railtie < ::Rails::Railtie
6
+ config.before_configuration do |app|
7
+ Integrations::Rails.new(app)
8
+ Integrations::FactoryBot.new(app)
9
+
10
+ # This is not used within packs-rails. Rather, this allows OTHER tools to
11
+ # hook into packs-rails via ActiveSupport hooks.
12
+ ActiveSupport.run_load_hooks(:packs_rails, Packs)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ require "packs-rails"
2
+
3
+ Packs::Rails::Integrations::RSpec.new
@@ -0,0 +1,42 @@
1
+ # typed: true
2
+
3
+ module Packs
4
+ module Rails
5
+ class Stim < Module
6
+ extend T::Sig
7
+
8
+ sig { params(pack: Packs::Pack, namespace: Module).void }
9
+ def initialize(pack, namespace)
10
+ @pack = pack
11
+ @namespace = namespace
12
+ super()
13
+ end
14
+
15
+ def included(engine)
16
+ engine.called_from = @pack.relative_path
17
+ engine.extend(ClassMethods)
18
+ engine.isolate_namespace(@namespace)
19
+
20
+ # Set all of these paths to nil because we want the Rails integration to take
21
+ # care of them. The purpose of this Engine is really just for the namespace
22
+ # isolation.
23
+ (Packs::Rails.config.paths +
24
+ # In addition to the paths we've delegated to the main app, we don't allow
25
+ # Engine Packs to have various capabilities.
26
+ %w(
27
+ config/environments
28
+ db/migrate
29
+ )
30
+ ).uniq.each do |path|
31
+ engine.paths[path] = nil
32
+ end
33
+ end
34
+
35
+ module ClassMethods
36
+ def find_root(_from)
37
+ T.unsafe(self).called_from
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,5 @@
1
+ module Packs
2
+ module Rails
3
+ VERSION = "0.0.1".freeze
4
+ end
5
+ end
@@ -0,0 +1,43 @@
1
+ require 'packs'
2
+ require "active_support"
3
+ require "rails/application"
4
+ require 'sorbet-runtime'
5
+
6
+ module Packs
7
+ module Rails
8
+ extend ActiveSupport::Autoload
9
+
10
+ autoload :Integrations
11
+ autoload :Railtie
12
+ autoload :Stim
13
+
14
+ class Error < StandardError; end
15
+
16
+ class << self
17
+ attr_reader :config
18
+
19
+ def root
20
+ @root ||= ::Rails::Application.find_root(Dir.pwd)
21
+ end
22
+ end
23
+
24
+ @config = ActiveSupport::OrderedOptions.new
25
+ @config.paths = %w(
26
+ app
27
+ app/controllers
28
+ app/channels
29
+ app/helpers
30
+ app/models
31
+ app/mailers
32
+ app/views
33
+ lib
34
+ lib/tasks
35
+ config
36
+ config/locales
37
+ config/initializers
38
+ config/routes
39
+ )
40
+ end
41
+
42
+ require "packs/rails/railtie"
43
+ end
metadata ADDED
@@ -0,0 +1,185 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: packs-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ngan Pham
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-01-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: railties
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: activesupport
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: packs
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
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: rake
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: rspec
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: debug
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
+ - !ruby/object:Gem::Dependency
98
+ name: sorbet
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: tapioca
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rails
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: packs-rails establishes and implements a set of conventions for splitting
140
+ up large monoliths.
141
+ email:
142
+ - gusto-opensource-buildkite@gusto.com
143
+ executables: []
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - README.md
148
+ - bin/console
149
+ - bin/setup
150
+ - bin/tapioca
151
+ - lib/packs-rails.rb
152
+ - lib/packs/rails/integrations.rb
153
+ - lib/packs/rails/integrations/factory_bot.rb
154
+ - lib/packs/rails/integrations/rails.rb
155
+ - lib/packs/rails/integrations/rspec.rb
156
+ - lib/packs/rails/railtie.rb
157
+ - lib/packs/rails/rspec.rb
158
+ - lib/packs/rails/stim.rb
159
+ - lib/packs/rails/version.rb
160
+ homepage: https://github.com/Gusto/packs-rails
161
+ licenses: []
162
+ metadata:
163
+ homepage_uri: https://github.com/Gusto/packs-rails
164
+ source_code_uri: https://github.com/Gusto/packs-rails
165
+ changelog_uri: https://github.com/Gusto/packs-rails/blob/main/CHANGELOG.md
166
+ post_install_message:
167
+ rdoc_options: []
168
+ require_paths:
169
+ - lib
170
+ required_ruby_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '2.7'
175
+ required_rubygems_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ requirements: []
181
+ rubygems_version: 3.1.6
182
+ signing_key:
183
+ specification_version: 4
184
+ summary: A Rails helper to package your code.
185
+ test_files: []