Feature Pack

Organizes and sets up the architecture of micro-applications within a Rails application, enabling the segregation of code, management, and isolation of functionalities, which can be developed, tested, and maintained independently of each other.

Javascript

Javascript must be on the FeaturePack root dir, not inside views dir.

Installation

Meanwhile installer isn't done, follow the steps below to install FeaturePack GEM:

# Add feature_pack to Gemfile
gem 'feature_pack'
bundle install

Setup loading

# config/application.rb

  feature_packs_path = Rails.root.join('app/feature_packs')
  FeaturePack.setup(features_path: feature_packs_path)

  FeaturePack.ignored_paths.each { |path| Rails.autoloaders.main.ignore(Rails.root.join(path)) }

  config.eager_load_paths << FeaturePack.features_path
  config.paths['app/views'] << FeaturePack.features_path

  config.paths['config/routes'] << (FeaturePack.path.to_s << '/feature_pack')
  config.paths['config/routes'] << FeaturePack.features_path
  config.assets.paths << FeaturePack.features_path.to_s

  Zeitwerk::Loader.eager_load_all

  config.after_initialize do
    load FeaturePack.path.join('feature_pack/group_controller.rb')
    load FeaturePack.path.join('feature_pack/controller.rb')

    FeaturePack.groups_controllers_paths.each { |group_controller_path| load group_controller_path }
    FeaturePack.features_controllers_paths.each { |controller_path| load controller_path }
  end
# initializers/feature_pack.rb

FeaturePack.groups.each do |group|
  group_module = FeaturePack.const_set(group.name.name.camelize, Module.new)

  %w[Lib AI Jobs].each do |submodule_name|
    submodule_path = File.join(group.relative_path, '_group_metadata', submodule_name.downcase)
    if Dir.exist?(submodule_path)
      submodule = group_module.const_set(submodule_name, Module.new)
      Rails.autoloaders.main.push_dir(submodule_path, namespace: submodule)
    end
  end

  group.features.each do |feature|
    feature_module = group_module.const_set(feature.name.name.camelize, Module.new)
    Rails.autoloaders.main.push_dir(feature.relative_path, namespace: feature_module)
  end
end
# app/helpers/application_helper.rb
  def feature_pack_group_path(group, *params) = send("feature_pack_#{group.name}_path".to_sym, *params)
  def feature_pack_path(group, feature, *params) = send("feature_pack_#{group.name}_#{feature.name}_path".to_sym, *params)
# config/initializers/assets.rb

Rails.application.config.assets.precompile += FeaturePack.javascript_files_paths
# config/importmap.rb

FeaturePack.javascript_files_paths.each do |js_file_path|
  extensionless_js_file_path = js_file_path.delete_suffix('.js')
  pin extensionless_js_file_path, to: extensionless_js_file_path, preload: false
end
# config/routes.rb
namespace :feature_pack, path: nil do
  draw :feature_pack_routes
end