Skip to content

Commit

Permalink
Fix reloading with custom classes (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
Exterm1nate authored Feb 22, 2025
1 parent b9fc55e commit 497392d
Show file tree
Hide file tree
Showing 10 changed files with 44 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
- Drop support for _Ruby_ < 3.1 (EOL)
- Added search functionality
- Added registry to store relationships between _Customizable_ types and _Active Field_ types
- Added notes about the necessity of disabling reloading for custom model classes and custom _Active Field_ type models
to prevent _STI_ (_Single Table Inheritance_) issues

**Breaking changes**:
- Maximum datetime precision reduced to 6 for all _Ruby_/_Rails_ versions.
Expand Down
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![Gem downloads count](https://img.shields.io/gem/dt/active_fields)](https://rubygems.org/gems/active_fields)
[![Github Actions CI](https://github.com/lassoid/active_fields/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/lassoid/active_fields/actions/workflows/main.yml)

**ActiveFields** is a _Rails_ plugin that implements the _Entity-Attribute-Value (EAV)_ pattern,
**ActiveFields** is a _Rails_ plugin that implements the _Entity-Attribute-Value_ (_EAV_) pattern,
enabling the addition of custom fields to any model at runtime without requiring changes to the database schema.

## Key Concepts
Expand Down Expand Up @@ -596,6 +596,25 @@ class CustomValue < ApplicationRecord
end
```
**Note:** To avoid _STI_ (_Single Table Inheritance_) issues in environments with code reloading (`config.enable_reloading = true`),
you should ensure that your custom model classes, along with all their superclasses and mix-ins, are non-reloadable.
Follow these steps:
- Move your custom model classes to a separate folder, such as `app/models/active_fields`.
- If your custom model classes subclass `ApplicationRecord` (or other reloadable class) or mix-in reloadable modules,
move those superclasses and modules to another folder, such as `app/models/core`.
- After organizing your files, add the following code to your `config/application.rb`:
```ruby
# Disable custom models reloading to avoid STI issues.
custom_models_dir = "#{root}/app/models/active_fields"
models_core_dir = "#{root}/app/models/core"
Rails.autoloaders.main.ignore(custom_models_dir, models_core_dir)
Rails.autoloaders.once.collapse(custom_models_dir, models_core_dir)
config.autoload_once_paths += [custom_models_dir, models_core_dir]
config.eager_load_paths += [custom_models_dir, models_core_dir]
```
This configuration disables namespaces for these folders
and adds them to `autoload_once_paths`, ensuring they are not reloaded.
### Adding Custom Field Types
To add a custom _Active Field_ type, create a subclass of the `ActiveFields.config.field_base_class`,
Expand Down Expand Up @@ -682,6 +701,9 @@ class IpArrayField < ActiveFields.config.field_base_class
end
```
**Note:** Similar to custom model classes, you should disable code reloading for custom _Active Field_ type models.
Place them in the `app/models/active_fields` folder too.
For each custom _Active Field_ type, you must define a **validator**, a **caster** and optionally a **finder**:
#### Validator
Expand Down
5 changes: 5 additions & 0 deletions app/models/active_fields/field.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

module ActiveFields
module Field; end
end
3 changes: 2 additions & 1 deletion lib/active_fields/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ class Engine < ::Rails::Engine

# Disable models reloading to avoid STI issues.
# Reloading can prevent subclasses from recognizing the base class.
config.autoload_once_paths = %W[#{root}/app/models #{root}/app/models/concerns]
config.autoload_once_paths << "#{root}/app/models"
config.autoload_once_paths << "#{root}/app/models/concerns"

initializer "active_fields.active_record" do
ActiveSupport.on_load(:active_record) do
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

# If the field base class is set to default, we disallow loading the custom class.
# If the field base class is set to default, we disallow loading the custom model class.
# This is an inversion of the same conditional statement from ActiveFields::Field::Base
if ActiveFields.config.field_base_class_changed?
class CustomField < ApplicationRecord
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

# If the value class is set to default, we disallow loading the custom class.
# If the value class is set to default, we disallow loading the custom model class.
# This is an inversion of the same conditional statement from ActiveFields::Value
if ActiveFields.config.value_class_changed?
class CustomValue < ApplicationRecord
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
10 changes: 10 additions & 0 deletions spec/dummy/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ class Application < Rails::Application
# Common ones are `templates`, `generators`, or `middleware`, for example.
config.autoload_lib(ignore: %w(assets tasks))

# Disable custom models reloading to avoid STI issues.
# Since custom models are subclasses of ApplicationRecord, it should also not be reloadable.
# Move it to `app/models/core`.
custom_models_dir = "#{root}/app/models/active_fields"
models_core_dir = "#{root}/app/models/core"
Rails.autoloaders.main.ignore(custom_models_dir, models_core_dir)
Rails.autoloaders.once.collapse(custom_models_dir, models_core_dir)
config.autoload_once_paths += [custom_models_dir, models_core_dir]
config.eager_load_paths += [custom_models_dir, models_core_dir]

# Force UTC time zone
config.time_zone = "UTC"
config.active_record.default_timezone = :utc
Expand Down

0 comments on commit 497392d

Please sign in to comment.