README.md in active_fields-1.0.0 vs README.md in active_fields-1.1.0
- old
+ new
@@ -7,13 +7,13 @@
**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
-- **Active Field**: A record with the definition of a custom field.
-- **Active Value**: A record that stores the value of an _Active Field_ for a specific _Customizable_.
-- **Customizable**: A record that has custom fields.
+- **Customizable**: A record that has custom fields (_Entity_).
+- **Active Field**: A record with the definition of a custom field (_Attribute_).
+- **Active Value**: A record that stores the value of an _Active Field_ for a specific _Customizable_ (_Value_).
## Models Structure
```mermaid
classDiagram
@@ -22,15 +22,15 @@
class ActiveField {
+ string name
+ string type
+ string customizable_type
- + json default_value
+ + json default_value_meta
+ json options
}
class ActiveValue {
- + json value
+ + json value_meta
}
class Customizable {
// This is your model
}
```
@@ -54,82 +54,94 @@
```
3. Add the `has_active_fields` method to any models where you want to enable custom fields:
```ruby
- class Author < ApplicationRecord
+ class Post < ApplicationRecord
has_active_fields
end
```
-4. Implement the necessary code to work with _Active Fields_.
+4. Run scaffold generator.
- This plugin provides a convenient API and helpers, allowing you to write code that meets your specific needs
+ This plugin provides a convenient API, allowing you to write code that meets your specific needs
without being forced to use predefined implementations that is hard to extend.
- Generally, you should:
- - Implement a controller and UI for managing _Active Fields_.
- - Add inputs for _Active Values_ in _Customizable_ forms and permit their params in the controller.
+ However, for a quick start, you can generate a scaffold by running the following command:
- To set _Active Values_ for your _Customizable_, use the `active_fields_attributes=` method,
- that integrates with Rails `fields_for` to generate appropriate form fields.
- Alternatively, the alias `active_fields=` can be used in contexts without `fields_for`, such as APIs.
+ ```shell
+ bin/rails generate active_fields:scaffold
+ ```
- To prepare a collection of _Active Values_ for use with the `fields_for` builder,
- call the `initialize_active_values` method.
+ This command generates a controller, routes and views for managing _Active Fields_,
+ along with form inputs for _Active Values_ and some useful helper methods.
- **Note:** By default, Rails form fields insert an empty string into array (multiple) parameters.
- You’ll need to handle the removal of these empty strings.
+ **Note:** Don't forget to add available _Customizable_ types in generated _Active Fields_ forms.
- ```ruby
- # app/controllers/posts_controller.rb
- # ...
+ **Note:** The array field helper uses _Stimulus_ for interactivity.
+ If your app doesn't already include _Stimulus_, you can [easily add it](https://github.com/hotwired/stimulus-rails).
+ Alternatively, if you prefer not to use _Stimulus_, you should implement your own JavaScript code.
- def new
- @post = Post.new
- @post.initialize_active_values
- end
+5. Add _Active Fields_ inputs in _Customizables_ forms and permit their params in controllers.
- def edit
- @post.initialize_active_values
- end
+ There are two methods available on _Customizable_ models for retrieving _Active Values_:
+ - `active_values` returns collection of only existing _Active Values_.
+ - `initialize_active_values` builds any missing _Active Values_ and returns the full collection.
- def post_params
- permitted_params = params.require(:post).permit(
- # ...
- active_fields_attributes: [:name, :value, :_destroy, value: []],
- )
- permitted_params[:active_fields_attributes]&.each do |_index, value_attrs|
- value_attrs[:value] = compact_array_param(value_attrs[:value]) if value_attrs[:value].is_a?(Array)
- end
+ Choose the method that suits your requirements.
+ In most cases, however, `initialize_active_values` is the more suitable option.
- permitted_params
- end
+ ```erb
+ # app/views/posts/_form.html.erb
+ # ...
- def compact_array_param(value)
- if value.first == ""
- value[1..-1]
- else
- value
- end
- end
- ```
+ <%= form.fields_for :active_fields, post.initialize_active_values.sort_by(&:active_field_id), include_id: false do |active_fields_form| %>
+ <%= active_fields_form.hidden_field :name %>
+ <%= render_active_value_input(form: active_fields_form, active_value: active_fields_form.object) %>
+ <% end %>
- ```erb
- # app/views/posts/_form.html.erb
- # ...
+ # ...
+ ```
- <%= form.fields_for :active_fields, post.active_values.sort_by(&:active_field_id), include_id: false do |active_fields_form| %>
- <%= active_fields_form.hidden_field :name %>
- # Render appropriate Active Value input and (optionally) destroy flag here
- <% end %>
+ Finally, permit the _Active Fields_ attributes in your _Customizables_ controllers:
- # ...
- ```
+ ```ruby
+ # app/controllers/posts_controller.rb
+ # ...
- You can find a detailed [example](https://github.com/lassoid/active_fields/blob/main/spec/dummy)
- of how to implement this in a full-stack Rails application.
+ def post_params
+ permitted_params = params.require(:post).permit(
+ # ...
+ active_fields_attributes: [:name, :value, :_destroy, value: []],
+ )
+ permitted_params[:active_fields_attributes]&.each do |_index, value_attrs|
+ value_attrs[:value] = compact_array_param(value_attrs[:value]) if value_attrs[:value].is_a?(Array)
+ end
+
+ permitted_params
+ end
+
+ # Removes an empty string from the beginning of the array parameter
+ def compact_array_param(value)
+ if value.first == ""
+ value[1..-1]
+ else
+ value
+ end
+ end
+ ```
+
+ **Note:** Here we use the `active_fields_attributes=` method (as a permitted parameter),
+ that integrates well with Rails `fields_for` to generate appropriate form fields.
+ Alternatively, the alias `active_fields=` can be used in contexts without `fields_for`, such as API controllers.
+
+ That's it!
+ You can now add _Active Fields_ to _Customizables_ at `http://localhost:3000/active_fields`
+ and fill in _Active Values_ within _Customizable_ forms.
+
+ You can also explore the [Demo app](https://github.com/lassoid/active_fields/blob/main/spec/dummy)
+ where the plugin is fully integrated into a full-stack Rails application.
Feel free to explore the source code and run it locally:
```shell
spec/dummy/bin/setup
bin/rails s
@@ -249,11 +261,11 @@
### Fields Base Attributes
- `name`(`string`)
- `type`(`string`)
- `customizable_type`(`string`)
-- `default_value` (`json`)
+- `default_value_meta` (`json`)
### Field Types Summary
All _Active Field_ model names start with `ActiveFields::Field`.
We replace it with `**` for conciseness.
@@ -493,10 +505,12 @@
This could cause existing _Active Values_ to become invalid,
leading to the associated _Customizables_ also becoming invalid,
which could potentially result in update failures.
+3. Only _Zeitwerk_ autoloading mode is supported.
+
## API Overview
### Fields API
```ruby
@@ -508,11 +522,11 @@
# Attributes:
active_field.type # Class name of this Active Field (utilizing STI)
active_field.customizable_type # Name of the Customizable model this Active Field is registered to
active_field.name # Identifier of this Active Field, it should be unique in scope of customizable_type
active_field.default_value_meta # JSON column declaring the default value. Consider using `default_value` instead
-active_field.options # A hash (json) containing type-specific attributes for this Active Field
+active_field.options # JSON column containing type-specific attributes for this Active Field
# Methods:
active_field.default_value # Default value for all Active Values associated with this Active Field
active_field.array? # Returns whether the Active Field type is an array
active_field.value_validator_class # Class used for values validation
@@ -521,11 +535,11 @@
active_field.value_caster # Caster object that performs values casting
active_field.customizable_model # Customizable model class
active_field.type_name # Identifier of the type of this Active Field (instead of class name)
# Scopes:
-ActiveFields::Field::Boolean.for("Author") # Collection of Active Fields registered for the specified Customizable type
+ActiveFields::Field::Boolean.for("Post") # Collection of Active Fields registered for the specified Customizable type
```
### Values API
```ruby
@@ -544,11 +558,11 @@
```
### Customizable API
```ruby
-customizable = Author.take
+customizable = Post.take
# Associations:
customizable.active_values # `has_many` association with Active Values linked to this Customizable
# Methods:
@@ -575,13 +589,14 @@
# Create, update or destroy Active Values.
# Implemented by `accepts_nested_attributes_for`.
# Please use `active_fields_attributes=`/`active_fields=` instead.
customizable.active_values_attributes = attributes
-# Build an Active Value, if it doesn't exist, with the default value for each Active Field.
+# Build not existing Active Values, with the default value for each Active Field.
+# Returns full collection of Active Values.
# This method is useful with `fields_for`, allowing you to pass the collection as an argument to render new Active Values:
-# `form.fields_for :active_values, customizable.active_values`.
+# `form.fields_for :active_fields, customizable.initialize_active_values`.
customizable.initialize_active_values
```
### Global Config
@@ -600,10 +615,10 @@
```
### Customizable Config
```ruby
-customizable_model = Author
+customizable_model = Post
customizable_model.active_fields_config # Access the Customizable's configuration
customizable_model.active_fields_config.customizable_model # The Customizable model itself
customizable_model.active_fields_config.types # Allowed Active Field types (e.g., `[:boolean]`)
customizable_model.active_fields_config.types_class_names # Allowed Active Field class names (e.g., `[ActiveFields::Field::Boolean]`)
```