GithubHelp home page GithubHelp logo

toptal / store_model Goto Github PK

View Code? Open in Web Editor NEW

This project forked from dmitrytsepelev/store_model

0.0 1.0 0.0 206 KB

Work with JSON-backed attributes as ActiveRecord-ish models

License: MIT License

Ruby 100.00%

store_model's Introduction

StoreModel Gem Version

StoreModel gem allows you to wrap JSON-backed DB columns with ActiveModel-like classes.

  • 💪 Powered with Attributes API. You can use a number of familiar types or write your own
  • 🔧 Works like ActiveModel. Validations, enums and nested attributes work very similar to APIs provided by Rails
  • 1️⃣ Follows single responsibility principle. Keep the logic around the data stored in a JSON column separated from the model
  • 👷‍♂️ Born in production.
class Configuration
  include StoreModel::Model

  attribute :model, :string
  enum :status, %i[active archived], default: :active

  validates :model, :status, presence: true
end

class Product < ApplicationRecord
  attribute :configuration, Configuration.to_type
end

Why should I wrap my JSON columns?

Imagine that you have a model Product with a jsonb column called configuration. This is how you likely gonna work with this column:

product = Product.find(params[:id])
if product.configuration["model"] == "spaceship"
  product.configuration["color"] = "red"
end
product.save

This approach works fine when you don't have a lot of keys with logic around them and just read the data. However, when you start working with that data more intensively–you may find the code a bit verbose and error-prone.

For instance, try to find a way to validate :model value to be required. Despite of the fact, that you'll have to write this validation by hand, it violates the single-responsibility principle: why parent model (Product) should know about the logic related to a child (Configuration)?

📖 Read more about the motivation in the Wrapping JSON-based ActiveRecord attributes with classes post

Getting started

Start with creating a class for representing the hash as an object:

class Configuration
  include StoreModel::Model

  attribute :model, :string
  attribute :color, :string
end

Attributes should be defined using Rails Attributes API. There is a number of types available out of the box, and you can always extend the type system.

Register the field in the ActiveRecord model class:

class Product < ApplicationRecord
  attribute :configuration, Configuration.to_type
end

When you're done, the initial snippet could be rewritten in the following way:

product = Product.find(params[:id])
if product.configuration.model == "spaceship"
  product.configuration.color = "red"
end
product.save

Usage note: Rails and assigning Arrays/Hashes to records

  • Assigned attributes must be a String, Hash, Array of Hashes, or StoreModel. For example, if the attributes are coming from a controller, be sure to convert any ActionController::Parameters as needed.
  • Any changes made to a StoreModel instance requires the attribute be flagged as dirty, either by reassignment (self.my_stored_models = my_stored_models.map(&:as_json)) or by will_change! (self.my_stored_models_will_change!)
  • Mixing StoreModel::NestedAttributes into your model will allow you to use accepts_nested_attributes_for in the same way as ActiveRecord.
class Supplier < ActiveRecord::Base
  include StoreModel::NestedAttributes

  has_many :bicycles, dependent: :destroy

  attribute :products, Product.to_array_type

  accepts_nested_attributes_for :bicycles, :products, allow_destroy: true
end

This will allow the form builders to work their magic:

<%= form_with model: @supplier do |form| %>
  <%= form.fields_for :products do |product_fields| %>
    <%= product_fields.text_field :name %>
  <% end %>
<% end %>

Resulting in:

<input type="text" name="supplier[products_attributes][0][name]" id="supplier_products_attributes_0_name">

In the controller:

def create
  @supplier = Supplier.new(supplier_params)
  @supplier.save
end

private

def supplier_params
  params.require(:supplier).permit(products_attributes: [:name])
end

Documentation

  1. Installation
  2. StoreModel::Model API:
  1. Array of stored models
  2. One of
  3. Alternatives
  4. Defining custom types

Credits

Initially sponsored by Evil Martians.

License

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

store_model's People

Contributors

dmitrytsepelev avatar rudskikhivan avatar skryukov avatar raphox avatar danielvdao avatar jonspalmer avatar jas14 avatar zooip avatar blaze182 avatar dush avatar petergoldstein avatar paneq avatar scottwater avatar codemogul avatar alexeevit avatar thomasklemm avatar timhwang21 avatar palkan avatar iarie avatar holywalley avatar zokioki avatar akinomaeni avatar morgangrubb avatar nikokon avatar osanay avatar agiveygives avatar supernich avatar bf4 avatar f-mer avatar flixt avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.