packs-rails 0.0.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: 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: []