Phlexi::Field is a field component framework for Ruby applications that provides a unified field abstraction across forms, displays, and tables. Built on a namespace-based architecture for handling complex object relationships. It's designed to work with Phlex, a framework for building view components in Ruby.
Phlexi::Field serves as the foundation for field rendering across multiple contexts in web applications. Unlike traditional form builders that focus solely on forms, Phlexi::Field creates a unified abstraction for working with fields that can be used for:
- Form inputs
- Read-only displays
- Table columns
- Any other UI context where object fields are presented
It is designed around a namespace-based architecture that creates a hierarchical tree structure for complex object graphs, handling nested relationships and collections gracefully.
Add this line to your application's Gemfile:
gem 'phlexi-field'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install phlexi-field
- Ruby >= 3.2.2
- Phlex ~> 2.0
- ActiveSupport >= 7.1
The foundation of Phlexi::Field is the Namespace
concept:
- A Namespace maps an object (like an ActiveRecord model) to a key
- Fields are created within a namespace
- Namespaces can be nested to represent complex object relationships
# Create a root namespace for a user object
namespace = Phlexi::Field::Structure::Namespace.new(
:user, # The key/name
parent: nil, # Root namespace has no parent
builder_klass: Phlexi::Field::Builder, # Field builder class
object: @user # The data object
)
# Create a field within the namespace
email_field = namespace.field(:email)
# Access field properties
email_field.dom.id # => "user_email"
email_field.dom.name # => "user[email]"
email_field.value # => The email value from @user
One of Phlexi::Field's strengths is handling complex object graphs:
# For a has_one relationship
namespace.nest_one(:profile) do |profile|
first_name = profile.field(:first_name)
# DOM properties: id="user_profile_first_name", name="user[profile][first_name]"
end
# For a has_many relationship
namespace.nest_many(:addresses) do |address_builder|
street = address_builder.field(:street)
# DOM properties: id="user_addresses_street", name="user[addresses][][street]"
end
Phlexi::Field separates field creation from rendering:
Builder
classes handle field creation, value access, and configurationComponent
classes handle presentation and rendering
This separation allows the same field structure to be rendered differently in various contexts.
Phlexi::Field provides a rich set of options for configuring how fields are displayed and behave:
Labels can be explicitly set or automatically generated from the object's attribute name:
# Custom label
field = namespace.field(:email, label: "Email Address")
# Get the current label (automatically falls back to humanized attribute name)
field.label # => "Email Address" or "Email" if not explicitly set
# Rails integration: Uses human_attribute_name if available
# user.class.human_attribute_name(:email) => "Email Address"
Hints provide contextual help for users, while descriptions offer more detailed explanations:
# Setting a hint for a field
field = namespace.field(:password, hint: "Must be at least 8 characters")
field.hint # => "Must be at least 8 characters"
field.has_hint? # => true
# Setting a description
field = namespace.field(:terms_accepted,
description: "By accepting our terms, you agree to our privacy policy.")
field.description # => "By accepting our terms, you agree to our privacy policy."
field.has_description? # => true
Text placeholders can be set for input fields:
field = namespace.field(:email, placeholder: "[email protected]")
field.placeholder # => "[email protected]"
Phlexi::Field automatically infers the appropriate field type based on multiple signals:
# Based on attribute/column type in ActiveRecord/ActiveModel
# email_field.inferred_field_type # => :string, :integer, :boolean, etc.
# For string fields, it can also infer more specific types
# email_field.inferred_string_field_type # => :email, :password, :url, etc.
The type inference algorithm considers:
- ActiveRecord column types
- ActiveModel attribute types
- Value type inspection
- Field name conventions (email, password, url, etc.)
- Validation rules (email format, numericality, etc.)
When using ActiveModel validations, Phlexi::Field can access them:
# Check if a field is required based on presence validators
field.required? # => true/false based on presence validators
# Access the validation errors
field.errors # => ["can't be blank", etc.]
For fields that support multiple selections:
field = namespace.field(:categories, multiple: true)
field.multiple? # => true
Phlexi::Field provides a foundation for building components that handle object fields such as forms and other UI components. It handles DOM structure, field values, configuration of labels, descriptions, hints, and more.
It serves as a foundation for Phlexi::Form, Phlexi::Display and Phlexi::Table.
class UserForm < Phlex::HTML
def initialize(user)
super()
@user = user
end
def view_template
namespace = Phlexi::Field::Structure::Namespace.new(
:user,
parent: nil,
builder_klass: Phlexi::Field::Builder,
object: @user
)
form(action: "/users", method: "post") do
# Create a name field
name_field = namespace.field(:name)
label(for: name_field.dom.id) { name_field.label }
input(
id: name_field.dom.id,
name: name_field.dom.name,
value: name_field.value,
type: "text"
)
# Create an email field
email_field = namespace.field(:email)
label(for: email_field.dom.id) { email_field.label }
input(
id: email_field.dom.id,
name: email_field.dom.name,
value: email_field.value,
type: "email"
)
# Submit button
button(type: "submit") { "Submit" }
end
end
end
# Usage in a controller
def new
user = User.new
render UserForm.new(user)
end
Phlexi::Field supports nested forms for complex data structures:
# For a has_one relationship
namespace.nest_one(:profile) do |profile|
profile.field(:first_name) # => Creates a field for user[profile][first_name]
end
# For a has_many relationship
namespace.nest_many(:addresses) do |address_builder|
address_field = address_builder.field(:id)
# Creates fields like user[addresses][][id]
end
Fields support various options for customization:
# Custom label
field = namespace.field(:email, label: "Email Address")
# Required fields
field = namespace.field(:email, required: true)
# Placeholder text
field = namespace.field(:email, placeholder: "Enter your email")
# Description/hint text
field = namespace.field(:password, hint: "Must be at least 8 characters")
Phlexi::Field comes with a base component architecture that you can extend:
class MyInputComponent < Phlexi::Field::Components::Base
def view_template
input(
id: field.dom.id,
name: field.dom.name,
value: field.value,
type: "text",
**attributes
)
end
end
# Usage
my_component = MyInputComponent.new(field, class: "custom-input")
render my_component
Phlexi::Field differs from traditional Rails form helpers and gems like Simple Form in several key ways:
- Unified Field API: Creates a consistent interface for fields across forms, displays, and tables
- Component-Based: Built on Phlex's component system rather than Rails' helper-based approach
- Separation of Concerns: Cleanly separates field structure (namespace), data handling (builder), and presentation (components)
- True Object Orientation: Works with complete objects and their relationships rather than flat attribute lists
While Phlexi::Field works standalone with Phlex, it has first-class support for Rails:
# In your Gemfile
gem 'phlexi-field'
gem 'phlex-rails'
The library automatically uses Rails conventions when available:
- Humanized attribute names via
human_attribute_name
- Association detection via
reflect_on_association
- Primary key handling
After checking out the repo, run bin/setup
to install dependencies. Then, run rake test
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
Bug reports and pull requests are welcome on GitHub at https://github.com/radioactive-labs/phlexi-field.
The gem is available as open source under the terms of the MIT License.