Skip to content

Ruact::Serializable

Ruact::Serializable is a mixin that lets you declare exactly which attributes of a Ruby object are safe to send to the browser as React props.


Why you need it

When you pass a Ruby object as a prop to a client component, ruact needs to serialize it to JSON. By default it calls as_json, which may expose internal fields, timestamps, or sensitive data you did not intend to send.

Ruact::Serializable gives you an explicit allowlist via ruact_props:

ruby
class Post < ApplicationRecord
  include Ruact::Serializable

  ruact_props :id, :title, :body
  # :admin_notes, :internal_score, etc. are never sent to the browser
end

Basic usage

ruby
class Post < ApplicationRecord
  include Ruact::Serializable

  ruact_props :id, :title, :body, :created_at
end

Use in an ERB template:

erb
<PostCard post={@post} />

The browser receives only { id: 1, title: "Hello", body: "...", created_at: "..." } — nothing else.


Works with any Ruby object

Ruact::Serializable is not limited to ActiveRecord models. Include it in any Ruby class:

ruby
class UserPresenter
  include Ruact::Serializable

  attr_reader :id, :display_name, :avatar_url, :role

  ruact_props :id, :display_name, :avatar_url, :role

  def initialize(user)
    @id           = user.id
    @display_name = user.full_name
    @avatar_url   = user.avatar.url
    @role         = user.admin? ? "admin" : "member"
  end
end
erb
<UserCard user={UserPresenter.new(@user)} />

ruact_props validates loudly — timing depends on the class

If you declare a prop that does not correspond to a defined method, ruact raises a clear ArgumentError naming the offending name:

ruby
class UserPresenter
  include Ruact::Serializable

  attr_reader :id, :name
  ruact_props :id, :name, :nonexistent_method
  # => ArgumentError: ruact_props: method `nonexistent_method` is not defined on UserPresenter
end

The loud check always happens; only when it fires depends on the class:

  • Plain Ruby objects (POROs) are checked at class load time — a typo or missing accessor raises the moment the class is loaded, at startup rather than at render time.
  • ActiveRecord models define their attribute reader methods lazily (on first access), so at the moment ruact_props :title runs the reader for a real column does not exist yet. To avoid rejecting a valid model at boot — and to avoid a database connection at class load — ruact defers the check for an AR model to the first serialize. A valid model boots; a bogus prop name still raises the same clean ArgumentError the first time that model is rendered.

Either way the guarantee is the same: a typo'd or missing prop fails loudly, never silently.


Nested serializable objects

Props can be nested. If a declared prop returns another object that includes Ruact::Serializable, it is serialized recursively using its own ruact_props declaration:

ruby
class Author < ApplicationRecord
  include Ruact::Serializable
  ruact_props :id, :name
end

class Post < ApplicationRecord
  include Ruact::Serializable
  ruact_props :id, :title, :author

  belongs_to :author
end
erb
<PostCard post={@post} />
# Sends: { id: 1, title: "Hello", author: { id: 5, name: "Alice" } }

Strict serialization mode

When config.strict_serialization = true, any object passed as a prop that does not include Ruact::Serializable raises Ruact::SerializationError.

This is the recommended production setting. It prevents passing raw ActiveRecord objects (which would call as_json) and ensures all prop surfaces are explicitly declared.

ruby
# config/initializers/ruact.rb
Ruact.configure do |config|
  config.strict_serialization = Rails.env.production?
end

ruact_props vs as_json

ruact_propsas_json
Explicit allowlist✅ Yes❌ No — serializes all attributes
Works on POROs and ActiveRecord models✅ Yes✅ Yes
Validated loudly for typos/missing accessors✅ Yes (POROs at load, AR at first serialize)❌ No
Enforced in strict mode✅ Yes❌ Raises in strict mode
Nested serialization✅ Recursive✅ Recursive (via as_json chain)

API

ruact_props(*attrs)

Class method — declares the attributes that are safe to serialize.

ruby
ruact_props :id, :name, :email
  • attrs — one or more Symbol names. Each must correspond to a reader method on the class (an attr_reader, a plain method, or an ActiveRecord column).
  • Raises ArgumentError for an undefined name — at class load for a PORO, at first serialize for an ActiveRecord model (whose readers are defined lazily). Either way it fails loudly.
  • Returns the list of declared props.

ruact_serialize

Instance method — returns a Hash of the declared props and their current values.

ruby
post = Post.find(1)
post.ruact_serialize  # => { id: 1, title: "Hello", body: "..." }

This is what ruact calls internally when serializing the object. You generally do not need to call it directly.

Released under the MIT License.