Feature Pack Gem
Feature Pack organizes and sets up the architecture of micro-applications within a Ruby on Rails application, enabling the segregation of code, management, and isolation of functionalities. Features can be developed, tested, and maintained independently of each other.
Installation
Add this line to your application's Gemfile:
gem 'feature_pack'And then execute:
bundle installSetup
Add to your config/application.rb:
# After Bundler.require
require 'feature_pack'
FeaturePack.setupConcepts
Groups
Groups are collections of related features. They provide:
- Shared layouts and partials
- Common base controller functionality
- Namespace organization
- Shared JavaScript modules
Features
Features are individual functionalities within a group. They provide:
- Independent controllers and views
- Isolated routes
- Feature-specific JavaScript
- Complete MVC structure
Usage
Creating a New Group
rails generate feature_pack:add_group human_resourcesThis creates the following structure:
app/feature_packs/
└── group_YYMMDD_human_resources/
└── _group_space/
├── controller.rb
├── manifest.yaml
├── routes.rb
├── javascript/
└── views/
├── index.html.slim
└── partials/
├── _header.html.slim
└── _footer.html.slim
Creating a New Feature
rails generate feature_pack:add_feature human_resources/employeesThis creates:
app/feature_packs/
└── group_YYMMDD_human_resources/
└── feature_YYMMDD_employees/
├── controller.rb
├── manifest.yaml
├── routes.rb
├── doc/
│ └── readme.md
├── javascript/
├── queries/
└── views/
├── index.html.slim
└── partials/
├── _header.html.slim
└── _footer.html.slim
Code Structure
Naming Convention
Groups
Format: group_<id>_<name>
-
group_- Required prefix -
<id>- Unique identifier (typically YYMMDD format) -
<name>- Group name in snake_case
Example: group_241209_human_resources
Features
Format: feature_<id>_<name>
-
feature_- Required prefix -
<id>- Unique identifier (typically YYMMDD format) -
<name>- Feature name in snake_case
Example: feature_241209_employees
Directory Structure
Group Space (_group_space)
Contains group-level resources:
-
controller.rb- Base controller for all features in the group -
manifest.yaml- Group configuration -
routes.rb- Group-level routes -
views/- Shared views and layouts -
javascript/- Shared JavaScript modules
Feature Directory
Contains feature-specific resources:
-
controller.rb- Feature controller -
manifest.yaml- Feature configuration -
routes.rb- Feature routes -
views/- Feature views -
javascript/- Feature-specific JavaScript -
queries/- Database queries -
doc/- Feature documentation
Controllers
Group Controller
class FeaturePack::HumanResourcesController < FeaturePack::GroupController
# Group-wide functionality
endFeature Controller
class FeaturePack::HumanResources::EmployeesController < FeaturePack::HumanResourcesController
def index
# Feature-specific logic
end
endGenerated feature controllers inherit their group's controller, so group callbacks
(authentication, authorization, etc.) apply to every feature. The setup callback
resolves the context from the routed path (feature_pack/<group>/<feature>): when the
second segment is a registered feature the request receives @group and @feature and
uses the feature's views; otherwise it receives @group and the group's views. No
extra declaration is needed in feature controllers.
Feature controllers that don't need a group controller can still inherit
FeaturePack::Controller, which requires the path to name a registered feature.
API-only features can inherit FeaturePack::API::Controller (an ActionController::API
subclass with no view setup). Load feature_pack/api/controller.rb in
config.after_initialize alongside the other base controllers (see SETUP.md).
The private hooks set_view_lookup_context_prefix and set_layout_paths can be
overridden in a group controller and apply to both group and feature requests.
Migrating from 0.10.x
- To share group callbacks, change a feature controller's superclass from
FeaturePack::ControllertoFeaturePack::<Group>Controller. -
__after_initialize.rbhooks are no longer loaded. Move that code to a Rails initializer or to the group/feature controller and delete the hook files. Leftover files are ignored by Zeitwerk, but they are dead code. -
FeaturePack.setupnow fails whenapp/feature_packsdoes not exist. Create the directory, or callFeaturePack.setup(require_features_path: false)to boot with no groups (a warning is logged).
Routes
Routes are automatically configured based on manifest files:
# Group manifest.yaml
url: /hr
name: Human Resources
# Feature manifest.yaml
url: /employees
name: Employees ManagementThis generates routes like:
-
/hr- Group index -
/hr/employees- Feature index
Helpers
Path Helpers
# Group path
feature_pack_group_path(:human_resources)
# => "/hr"
# Feature path
feature_pack_path(:human_resources, :employees)
# => "/hr/employees"
# With parameters
feature_pack_path(:human_resources, :employees, id: 1)
# => "/hr/employees?id=1"Controller Variables
Available in controllers and views:
-
@group- Current group object -
@feature- Current feature object (not available in group controller)
View Hierarchy
Views are resolved in the following order:
- Feature-specific views
- Group-shared views
- Application default views
Partials
Header and footer partials follow a fallback pattern:
- Feature's
views/partials/_header.html.slim - Group's
views/partials/_header.html.slim - Application's default header
JavaScript Integration
JavaScript files are automatically discovered and can be referenced:
# In views
javascript_include_tag @group.javascript_module('shared')
javascript_include_tag @feature.javascript_module('employees')Advanced Configuration
Manifest Files
Group manifest (_group_space/manifest.yaml):
url: /hr
name: Human Resources
const_aliases:
- employee_model: Employee
- department_model: DepartmentFeature manifest:
url: /employees
name: Employees Management
const_aliases:
- service: EmployeeServiceConst Aliases
Access aliased constants:
# In controllers
@group.employee_model # => Employee
@feature.service # => FeaturePack::HumanResources::Employees::EmployeeServiceBest Practices
- Group Organization: Group related features that share common functionality
- Naming: Use descriptive snake_case names for groups and features
- Isolation: Keep features independent and loosely coupled
- Shared Resources: Place common code in group space
-
Documentation: Document each feature in its
doc/readme.md
Troubleshooting
Common Issues
-
Group/Feature Not Found
- Ensure proper naming convention
- Run
FeaturePack.setupafter adding new groups/features - Check manifest files exist
-
Routes Not Working
- Verify manifest.yaml has correct URL configuration
- Check routes.rb files exist
- Restart Rails server after changes
-
Views Not Rendering
- Check view file extensions (.html.slim, .html.erb, etc.)
- Verify view paths in controller
- Check for typos in view names
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create new Pull Request
License
The gem is available as open source under the terms of the MIT License.
Author
Gedean Dias - gedean.dias@gmail.com
Links
Tests
bundle install
bundle exec rspecThe suite covers discovery, setup rollback, first-time generation, duplicate protection, inherited authorization, and Rails request rendering using ERB fixtures.