Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
S

shallow_attributes

> 编程语言
Open source

Simple and lightweight Virtus analog.

100 stars0 likes1 views
WebsiteGitHub

About

Simple and lightweight Virtus analog.

ShallowAttributes

Simple and lightweight Virtus analog without any dependencies. Documentation.

Motivation

There are already a lot of good and flexible gems which solve a similar problem, allowing attributes to be defined with their types, for example: virtus, fast_attributes or attrio. However, the disadvantage of these gems is performance or API. So, the goal of ShallowAttributes is to provide a simple solution which is similar to the Virtus API, simple, fast, understandable and extendable.

  • This is the performance benchmark of ShallowAttributes compared to Virtus gems.
  • Default ruby struct, dry-struct, virtus and ShallowAttributes ips and memory benchmarks

Installation

Add this line to your application's Gemfile:

ruby
gem 'shallow_attributes'

And then execute:

$ bundle

Or install it yourself as:

$ gem install shallow_attributes

Examples

Table of contents

  • Using ShallowAttributes with Classes
  • Default Values
  • Mandatory Attributes
  • Embedded Value
  • Custom Coercions
  • Collection Member Coercions
  • Note about Member Coercions
  • Overriding setters
  • ActiveModel compatibility
  • Dry-types

Using ShallowAttributes with Classes

You can create classes extended with Virtus and define attributes:

ruby
class User
  include ShallowAttributes

  attribute :name, String
  attribute :age, Integer
  attribute :birthday, DateTime
end

class SuperUser  "Anton"

user.age = '31' # => 31
user.age = nil  # => nil
user.age.class  # => Fixnum

user.birthday = 'November 18th, 1983' # => #

user.attributes # => { name: "Anton", age: 31, birthday: nil }

# mass-assignment
user.attributes = { name: 'Jane', age: 21 }
user.name       # => "Jane"
user.age        # => 21

super_user = SuperUser.new
user.age = nil  # => 0

ShallowAttributes doesn't make any assumptions about base classes. There is no need to define default attributes, or even mix ShallowAttributes into the base class:

ruby
require 'active_model'

class Form
  extend ActiveModel::Naming
  extend ActiveModel::Translation
  include ActiveModel::Conversion
  include ShallowAttributes

  def persisted?
    false
  end
end

class SearchForm  "Anton"

Default Values

…

Mandatory attributes

You can provide present: true option for any attribute that will prevent class from initialization if this attribute was not provided:

ruby
class CreditCard
  include ShallowAttributes
  attribute :number, Integer, present: true
  attribute :owner, String, present: true
end

card = CreditCard.new(number: 1239342)
# => ShallowAttributes::MissingAttributeError: Mandatory attribute "owner" was not provided

Embedded Value

ruby
class City
  include ShallowAttributes

  attribute :name, String
  attribute :size, Integer, default: 9000
end

class Address
  include ShallowAttributes

  attribute :street,  String
  attribute :zipcode, String, default: '111111'
  attribute :city,    City
end

class User
  include ShallowAttributes

  attribute :name,    String
  attribute :address, Address
end

user = User.new(address: {
  street: 'Street 1/2',
  zipcode: '12345',
  city: {
    name: 'NYC'
  }
})

user.address.street    # => "Street 1/2"
user.address.city.name # => "NYC"

Custom Coercions

…

Collection Member Coercions

…

IMPORTANT note about member coercions

ShallowAttributes performs coercions only when a value is being assigned. If you mutate the value later on using its own interfaces then coercion won't be triggered.

Here's an example:

ruby
class Book
  include ShallowAttributes
  attribute :title, String
end

class Library
  include ShallowAttributes
  attribute :books, Array, of: Book
end

library = Library.new

# This will coerce Hash to a Book instance
library.books = [ { :title => 'Introduction' } ]

# This WILL NOT COERCE the value because you mutate the books array with Array# 'Another Introduction' }

Overriding setters

ruby
class User
  include ShallowAttributes

  attribute :name, String

  alias_method :_name=, :name=
  def name=(new_name)
    custom_name = nil
    if new_name == "Godzilla"
      custom_name = "Can't tell"
    end

    self._name = custom_name || new_name
  end
end

user = User.new(name: "Frank")
user.name # => 'Frank'

user = User.new(name: "Godzilla")
user.name # => 'Can't tell'

ActiveModel compatibility

ShallowAttributes is fully compatible with ActiveModel.

Form object

ruby
require 'active_model'

class SearchForm
  extend ActiveModel::Naming
  extend ActiveModel::Translation
  include ActiveModel::Conversion
  include ShallowAttributes

  attribute :name, String
  attribute :service_ids, Array, of: Integer
  attribute :archived, 'Boolean', default: false

  def persisted?
    false
  end

  def results
    # ...
  end
end

class SearchesController 
  
  
  

Validations

ruby
require 'active_model'

class Children
  include ShallowAttributes
  include ActiveModel::Validations

  attribute :scream, String
  validates :scream, presence: true
end

user = User.new(scream: '')
user.valid? # => false
user.scream = 'hello world!'
user.valid? # => true

Dry-types

You can use dry-types objects as a type for your attribute:

ruby
module Types
  include Dry::Types.module
end

class User
  include ShallowAttributes

  attribute :name, Types::Coercible::String
  attribute :age, Types::Coercible::Int
  attribute :birthday, DateTime
end

user = User.new(name: nil, age: 0)
user.name # => ''
user.age # => 0

Ruby version support

ShallowAttributes is known to work correctly with the following rubies:

  • 2.0
  • 2.1
  • 2.2
  • 2.3
  • 2.4
  • jruby-head

Also we run rbx-2 buld too.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/davydovanton/shallow_attributes. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Rubydata-objectrubyvirtus

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 18, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言