• Stars
    star
    344
  • Rank 118,399 (Top 3 %)
  • Language
    Ruby
  • License
    MIT License
  • Created almost 8 years ago
  • Updated 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

ActiveRecord extension which adds typecasting to store accessors

Cult Of Martians Gem Version Build

Store Attribute

ActiveRecord extension which adds typecasting to store accessors.

Originally extracted from not merged PR to Rails: rails/rails#18942.

Install

In your Gemfile:

# for Rails 6.1+ (7 is supported)
gem "store_attribute", "~> 1.0"

# for Rails 5+ (6 is supported)
gem "store_attribute", "~> 0.8.0"

# for Rails 4.2
gem "store_attribute", "~> 0.4.0"

Usage

You can use store_attribute method to add additional accessors with a type to an existing store on a model.

store_attribute(store_name, name, type, options)

Where:

  • store_name The name of the store.
  • name The name of the accessor to the store.
  • type A symbol such as :string or :integer, or a type object to be used for the accessor.
  • options (optional) A hash of cast type options such as precision, limit, scale, default. Regular store_accessor options, such as prefix, suffix are also supported.

Type casting occurs every time you write data through accessor or update store itself and when object is loaded from database.

Note that if you update store explicitly then value isn't type casted.

Examples:

class MegaUser < User
  store_attribute :settings, :ratio, :integer, limit: 1
  store_attribute :settings, :login_at, :datetime
  store_attribute :settings, :active, :boolean
  store_attribute :settings, :color, :string, default: "red"
  store_attribute :settings, :colors, :json, default: ["red", "blue"]
  store_attribute :settings, :data, :datetime, default: -> { Time.now }
end

u = MegaUser.new(active: false, login_at: "2015-01-01 00:01", ratio: "63.4608")

u.login_at.is_a?(DateTime) # => true
u.login_at = DateTime.new(2015, 1, 1, 11, 0, 0)
u.ratio # => 63
u.active # => false
# Default value is set
u.color # => red
# Default array is set
u.colors # => ["red", "blue"]
# A dynamic default can also be provided
u.data # => Current time
# And we also have a predicate method
u.active? # => false
u.reload

# After loading record from db store contains casted data
u.settings["login_at"] == DateTime.new(2015, 1, 1, 11, 0, 0) # => true

# If you update store explicitly then the value returned
# by accessor isn't type casted
u.settings["ratio"] = "3.141592653"
u.ratio # => "3.141592653"

# On the other hand, writing through accessor set correct data within store
u.ratio = "3.141592653"
u.ratio # => 3
u.settings["ratio"] # => 3

You can also specify type using usual store_accessor method:

class SuperUser < User
  store_accessor :settings, :privileges, login_at: :datetime
end

Or through store:

class User < ActiveRecord::Base
  store :settings, accessors: [:color, :homepage, login_at: :datetime], coder: JSON
end

Using defaults

With store_attribute, you can provide default values for the store attribute. This functionality follows Rails behaviour for attribute ..., default: ... (and is backed by Attribute API).

You must remember two things when using defaults:

  • A default value is only populated if no value for the store attribute was set, i.e., only when creating a new record.
  • Default values persist as soon as you save the record.

The examples below demonstrate these caveats:

# Database schema
create_table("users") do |t|
  t.string :name
  t.jsonb :extra
end

class RawUser < ActiveRecord::Base
  self.table_name = "users"
end

class User < ActiveRecord::Base
  attribute :name, :string, default: "Joe"
  store_attribute :extra, :expired_at, :date, default: -> { 2.days.from_now }
end

Date.current #=> 2022-03-17

user = User.new
user.name #=> "Joe"
user.expired_at #=> 2022-03-19
user.save!

raw_user = RawUser.find(user.id)
raw_user.name #=> "Joe"
raw_user.expired_at #=> 2022-03-19

another_raw_user = RawUser.create!
another_user = User.find(another_raw_user.id)

another_user.name #=> nil
another_user.expired_at #=> nil

It is possible to configure store_attribute to return the default value even when the record is persisted and the attribute name is not present. By using the store_attribute_unset_values_fallback_to_default class option, default values will be returned for missing keys. For example:

class User < ApplicationRecord
  self.store_attribute_unset_values_fallback_to_default = true
end

user = User.create!(extra: {})
user.expired_at #=> 2022-03-19

IMPORTANT: Due to implementation limitations, it's not recommended to toggle the value of store_attribute_unset_values_fallback_to_default in sub-classes. We recommend to set this value in base classes (e.g., ApplicationRecord).

More Repositories

1

logidze

Database changes log for Rails
Ruby
1,555
star
2

action_policy

Authorization framework for Ruby/Rails applications
Ruby
1,324
star
3

isolator

Detect non-atomic interactions within DB transactions
Ruby
814
star
4

anyway_config

Configuration library for Ruby gems and applications
Ruby
719
star
5

active_delivery

Ruby framework for keeping all types of notifications (mailers, push notifications, whatever) in one place
Ruby
585
star
6

n_plus_one_control

RSpec and Minitest matchers to prevent N+1 queries problem
Ruby
543
star
7

view_component-contrib

A collection of extension and developer tools for ViewComponent
Ruby
318
star
8

litecable

Lightweight Action Cable implementation (Rails-free)
Ruby
285
star
9

acli

Action Cable command-line client
Ruby
222
star
10

action-cable-testing

Action Cable testing utils
Ruby
209
star
11

rubanok

Parameters-based transformation DSL
Ruby
200
star
12

active_event_store

Rails Event Store in a more Rails way
Ruby
167
star
13

action_policy-graphql

Action Policy integration for GraphQL
Ruby
149
star
14

engems

Rails component-based architecture on top of engines and gems (showroom)
Ruby
136
star
15

influxer

InfluxDB ActiveRecord-style
Ruby
118
star
16

abstract_notifier

ActionMailer-like interface for any type of notifications
Ruby
116
star
17

wsdirector

All the world's a server, and all the men and women merely clients
Ruby
99
star
18

pgrel

ActiveRecord extension for querying hstore and jsonb
Ruby
93
star
19

gem-check

GemCheck: Writing Better Ruby Gems Checklist
CSS
93
star
20

turbo-music-drive

Exploring Turbo future features while building a music library app
Ruby
89
star
21

rbytes

Ruby Bytes helps you build, deploy and install Ruby and Rails application templates
Ruby
65
star
22

faqueue

Researching background jobs fairness
Ruby
63
star
23

downstream

Straightforward way to implement communication between Rails Engines using the Publish-Subscribe pattern.
Ruby
47
star
24

influx_udp

Erlang InfluxDB UDP writer
Erlang
31
star
25

newgem

Custom script to generate new gems
Ruby
30
star
26

ruby-dip

Docker-based development environment for hacking Ruby MRI
Dockerfile
30
star
27

turbo-view-transitions

View Transitions API for Turbo
TypeScript
28
star
28

erlgrpc

GRPC client for Erlang
Erlang
25
star
29

as3_p2plocal

as3 lib for local p2p connections (serverless rtmfp)
ActionScript
25
star
30

rails-intest-views

Generate view templates dynamically in Rails tests
Ruby
20
star
31

sharelatex-vagrant-ansible

Vagrant + Ansible configuration for ShareLatex
Shell
12
star
32

docsify-namespaced

Docsify plugin to work with namespaces
JavaScript
11
star
33

docs-example

Playground for dealing with documentation engines
7
star
34

ruby-russia-2020

RubyRussia 2020 "Frontendless Rails" workshop demo app
Ruby
6
star
35

engine-cable-app

Experimenting with Action Cable and engines
Ruby
6
star
36

palkan

It's me
4
star
37

ruby-compatibility-examples

Collections of reproduction cases for TruffleRuby vs. MRI (in)compatibility
Ruby
3
star
38

erffmpeg

Erlang wrapper for some ffmpeg
C
3
star
39

th-dummy

TH Dummy
Ruby
2
star
40

ulitos

Erlang utils modules
Erlang
2
star
41

meetings

Good old Teachbase Meetings client
ActionScript
2
star
42

macos-setup

Shell
1
star
43

bitrix-orm

Bitrix kinda ORM for IBlockElements and CUser.
PHP
1
star
44

adventofcode2018

https://adventofcode.com
Rust
1
star
45

tb_utils

ActionScript 3 library
ActionScript
1
star
46

rebar_templates

Custom rebar templates
Erlang
1
star