### Complete Application Example with Models, Migration, and Usage Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/index.md This snippet demonstrates the full setup of AttrJson, including defining nested models (Address, Contact), a main model (Person) with typed JSON attributes, an ActiveRecord migration for storing JSONB data, and example usage for creating and querying records. ```ruby class Address include AttrJson::Model attr_json :street, :string attr_json :city, :string attr_json :postal_code, :string validates :street, :city, :postal_code, presence: true end class Contact include AttrJson::Model attr_json :email, :string attr_json :phone, :string validates :email, format: { with: URI::MailTo::EMAIL_REGEXP } end class Person < ApplicationRecord include AttrJson::Record include AttrJson::Record::QueryScopes include AttrJson::NestedAttributes attr_json_config(default_accepts_nested_attributes: true) attr_json :first_name, :string attr_json :last_name, :string attr_json :email, :string attr_json :address, Address.to_type attr_json :contact, Contact.to_type attr_json :verified, :boolean, default: false attr_json_accepts_nested_attributes_for :address, :contact validates :first_name, :last_name, :email, presence: true scope :verified, -> { jsonb_contains(verified: true) } end # Migration class CreatePeople < ActiveRecord::Migration[6.0] def change create_table :people do |t| t.string :first_name t.string :last_name t.string :email t.jsonb :json_attributes t.timestamps end add_index :people, :json_attributes, using: :gin end end # Usage person = Person.new( first_name: "Jane", last_name: "Doe", email: "jane@example.com", address: { street: "123 Main St", city: "New York", postal_code: "10001" }, contact: { email: "jane@work.com", phone: "555-1234" } ) person.save! # Query Person.verified.jsonb_contains("address.city" => "New York") ``` -------------------------------- ### Introspection Example Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md An example demonstrating how to list all attributes with their types. ```APIDOC ## List All Attributes with Types ### Example ```ruby Article.attr_json_registry.definitions.each do |defn| puts "#{defn.name}: #{defn.type.class.name}" end ``` ``` -------------------------------- ### String Type Casting Examples Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/types.md Demonstrates casting various values to the String type. ```ruby # String type "hello".cast # => "hello" 123.cast # => "123" nil.cast # => nil ``` -------------------------------- ### Model Type with strip_nils Examples Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/types.md Provides examples of using `strip_nils` with model types. Demonstrates `:safely` for attributes with defaults and `true` to force stripping of all nils. ```ruby class Author include AttrJson::Model attr_json :name, :string attr_json :bio, :string, default: "" end class Book include AttrJson::Record # Bio has a default, so nil is important - use :safely attr_json :author, Author.to_type(strip_nils: :safely) # Force all nils removed attr_json :editor, Author.to_type(strip_nils: true) end ``` -------------------------------- ### Model Serialization Example Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/types.md Shows how attributes of different types are serialized into JSON-compatible values when saved to the database. ```ruby class Event include AttrJson::Record attr_json :name, :string attr_json :happened_at, :datetime attr_json :duration_days, :integer end event = Event.new( name: "Conference", happened_at: Time.zone.parse("2024-06-15 09:00"), duration_days: 3 ) event.save! # JSON stored as: # { # "name": "Conference", # "happened_at": "2024-06-15T09:00:00Z", # "duration_days": 3 # } ``` -------------------------------- ### Example of Arbitrary JSON Data Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/types.md Instantiate a model with arbitrary JSON data and access the metadata attribute. ```ruby article = Article.new(metadata: { arbitrary: "data", nested: { key: "value" } }) article.metadata # => {"arbitrary"=>"data", "nested"=>{"key"=>"value"}} ``` -------------------------------- ### Database Migration for JSON Columns Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/index.md Provides an example of a Rails migration to create tables with JSONB columns and add GIN indexes for efficient querying. ```ruby class CreateArticles < ActiveRecord::Migration[6.0] def change create_table :articles do |t| t.string :title t.jsonb :json_attributes t.jsonb :metadata t.timestamps end # Index for queries add_index :articles, :json_attributes, using: :gin add_index :articles, :metadata, using: :gin end end ``` -------------------------------- ### Polymorphic Models Example Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/index.md Demonstrates how to define a record with an attribute that can hold an array of different types of polymorphic models. ```ruby class CD include AttrJson::Model attr_json :title, :string attr_json :artist, :string end class Book include AttrJson::Model attr_json :title, :string attr_json :author, :string end class Shelf include AttrJson::Record attr_json :items, AttrJson::Type::PolymorphicModel.new(CD, Book), array: true end ``` -------------------------------- ### Array of Models Example Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/index.md Illustrates defining a record with an attribute that holds an array of nested models. ```ruby class Item include AttrJson::Model attr_json :name, :string attr_json :quantity, :integer end class Order include AttrJson::Record attr_json :items, Item.to_type, array: true end ``` -------------------------------- ### Boolean Type Casting Examples Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/types.md Demonstrates casting various string and numeric representations to the Boolean type. ```ruby # Boolean type "true".cast # => true "false".cast # => false "yes".cast # => true 1.cast # => true 0.cast # => false ``` -------------------------------- ### Integer Type Casting Examples Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/types.md Demonstrates casting various values to the Integer type, including handling invalid input. ```ruby # Integer type "42".cast # => 42 42.0.cast # => 42 "invalid".cast # => nil (or error depending on config) ``` -------------------------------- ### Type Casting Example Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Record.md Demonstrates how string values are automatically cast to their appropriate types (e.g., integer, Time) when assigned to attributes, similar to ActiveRecord. ```ruby article = Article.new article.view_count = "42" article.view_count # => 42 (cast to integer) article.published_at = "2024-01-15 10:30:00" article.published_at # => Time object representing that datetime ``` -------------------------------- ### Get All Attribute Definitions Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Returns an array containing all AttributeDefinition objects registered for the model. ```ruby def definitions ``` ```ruby Article.attr_json_registry.definitions.map(&:name) # => [:title, :created_at, :view_count] ``` -------------------------------- ### Using AttrJson::Record::QueryBuilder Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/QueryScopes.md For advanced querying needs, you can directly utilize the `QueryBuilder` class. This example shows how to instantiate the builder and obtain a relation for containment queries. ```ruby builder = AttrJson::Record::QueryBuilder.new(Article, title: "Hello") relation = builder.contains_relation # relation is a chainable ActiveRecord::Relation ``` -------------------------------- ### Database Indexing for JSONB Columns Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/QueryScopes.md For optimal query performance on large datasets, create GIN indexes on your JSONB columns. This example shows how to add a GIN index during table creation or to an existing table. ```ruby # In migration create_table :articles do |t| t.jsonb :json_attributes t.index :json_attributes, using: :gin end # Or add to existing table add_index :articles, :json_attributes, using: :gin ``` -------------------------------- ### Single Nested Model Example Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/index.md Shows how to define a model with a single nested model attribute, using AttrJson::Model and AttrJson::Record. ```ruby class Contact include AttrJson::Model attr_json :name, :string attr_json :email, :string end class Company include AttrJson::Record attr_json :name, :string attr_json :primary_contact, Contact.to_type end ``` -------------------------------- ### Get Original Arguments Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Access the original arguments passed during the definition's creation, useful for debugging or reflection. ```ruby attr_reader :original_args ``` ```ruby definition.original_args # => [:title, :string, {default: "Untitled"}] ``` -------------------------------- ### Get Attribute Name Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Retrieve the attribute's name as a symbol. ```ruby attr_reader :name ``` ```ruby definition.name # => :title ``` -------------------------------- ### Get Raw Default Argument Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Retrieve the default value or proc as it was originally defined, before any calling or type casting. ```ruby def default_argument end ``` ```ruby definition.default_argument # => "Untitled" or Proc or nil ``` -------------------------------- ### Simple Form Input for String Array Source: https://github.com/jrochkind/attr_json/blob/master/doc_src/forms.md Example of using Simple Form to render text fields for each element in a string array attribute. This approach handles display, submission, and updates. ```erb <%= f.input :string_array do %> <% f.object.string_array.each do |str| %> <%= f.text_field(:string_array, value: str, class: "form-control", multiple: true) %> <% end %> <% end %> ``` -------------------------------- ### Get Attribute Definition by Key Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Retrieves a specific attribute definition from the registry using its attribute name. Accepts both symbols and strings as keys. ```ruby def [](key) ``` ```ruby Article.attr_json_registry[:title] # => AttributeDefinition Article.attr_json_registry["title"] # => AttributeDefinition ``` -------------------------------- ### Get All Registered Attribute Names Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Returns an array of symbols representing all attribute names that have been registered for the model. ```ruby def attribute_names ``` ```ruby Article.attr_json_registry.attribute_names # => [:title, :created_at, :view_count] ``` -------------------------------- ### Get All Attribute Names (Instance) Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Model.md Retrieves an array of all attribute names for the model instance. Note: This is distinct from the class method `attribute_names`. ```ruby def attribute_names ``` -------------------------------- ### Querying with Custom Store Keys Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/QueryScopes.md When using custom `store_key` in attribute definitions, queries should use the attribute name, not the store key. This example shows how to define an attribute with a custom store key and then query it using the attribute name. ```ruby class Article include AttrJson::Record include AttrJson::Record::QueryScopes attr_json :author_name, :string, store_key: "__author" end # Query by attribute name, not store_key Article.jsonb_contains(author_name: "Jane") # Internally queries: {"__author":"Jane"} ``` -------------------------------- ### Get All Store Keys in a Container Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Iterates through attribute definitions and prints the name and store key for attributes stored within a specific container, like 'json_attributes'. ```ruby Article.attr_json_registry.definitions.each do |defn| if defn.container_attribute == "json_attributes" puts "#{defn.name} => #{defn.store_key}" end end ``` -------------------------------- ### Define Nested Models Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Model.md Illustrates how to define arbitrarily nested AttrJson::Model structures. This example shows a Company model containing an Address, which in turn contains GeoLocation. ```ruby class GeoLocation include AttrJson::Model attr_json :latitude, :float attr_json :longitude, :float end class Address include AttrJson::Model attr_json :street, :string attr_json :location, GeoLocation.to_type end class Company include AttrJson::Record attr_json :name, :string attr_json :headquarters, Address.to_type end ``` -------------------------------- ### Querying Across Multiple JSON Containers Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/QueryScopes.md When your model uses multiple JSON columns, queries automatically resolve to the correct container. This example demonstrates querying attributes from different JSON containers within the same model. ```ruby class Article include AttrJson::Record include AttrJson::Record::QueryScopes attr_json_config(default_container_attribute: :json_attributes) attr_json :title, :string attr_json :view_count, :integer attr_json :seo_title, :string, container_attribute: :seo_data attr_json :seo_description, :string, container_attribute: :seo_data end # Queries both containers appropriately Article.jsonb_contains( title: "Hello", seo_title: "Search Title" ) # WHERE (... json_attributes @> ...) AND (... seo_data @> ...) ``` -------------------------------- ### Create a New Attribute Definition Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Instantiate an `AttrJson::AttributeDefinition` manually. This is typically handled by the `attr_json` macro, but can be used for advanced introspection or custom setups. ```ruby def initialize(name, type, options = {}) end ``` ```ruby # Normally done via attr_json, but for advanced use: definition = AttrJson::AttributeDefinition.new( :title, :string, { default: "Untitled", store_key: "heading" } ) ``` -------------------------------- ### Define Model with ActiveModel Validations Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Model.md Use AttrJson::Model to define attributes and integrate ActiveModel validations. This example shows presence and format validations for contact information. ```ruby class Contact include AttrJson::Model attr_json :name, :string attr_json :email, :string validates :name, presence: true validates :email, format: { with: URI::MailTo::EMAIL_REGEXP } end contact = Contact.new contact.valid? # => false contact.errors # => # ``` -------------------------------- ### Define Custom Setter for Early Error Raising Source: https://github.com/jrochkind/attr_json/blob/master/README.md Provides an example of defining a custom setter method for `some_json_column` in an ActiveRecord model. This allows for custom casting logic and early error raising for data problems. ```ruby class MyTable < ApplicationRecord serialize :some_json_column, MyModel.to_serialization_coder def some_json_column=(val) super( ) end end ``` -------------------------------- ### Setting and Accessing Configuration Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/configuration.md Demonstrates how to set configuration options using attr_json_config and access them on a model. ```ruby class MyModel include AttrJson::Record # Set configuration attr_json_config(default_container_attribute: "data", default_accepts_nested_attributes: true) # Access configuration MyModel.attr_json_config.default_container_attribute # => "data" end ``` -------------------------------- ### Get Serialization Coder for Rails serialize Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Model.md Retrieve a serialization coder for use with Rails' `serialize` feature, enabling entire JSON columns to store complex AttrJson::Model objects. This simplifies persistence of structured data. ```ruby class MyModel include AttrJson::Model attr_json :title, :string attr_json :content, :string end class Article < ApplicationRecord serialize :metadata, MyModel.to_serialization_coder end article = Article.new(metadata: MyModel.new(title: "Title", content: "Content")) article.save! # Entire model serialized to column ``` -------------------------------- ### initialize Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Model.md Creates a new model instance, initializing it with the provided attributes. ```APIDOC ## initialize ### Description Create a new model instance with given attributes. ### Method `initialize(attributes = {})` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body * **attributes** (Hash) - Attributes to assign ### Request Example ```ruby contact = Contact.new(name: "John", email: "john@example.com") ``` ``` -------------------------------- ### Configuration Inheritance and Overriding Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/configuration.md Illustrates how configuration is inherited by subclasses and can be overridden. ```ruby class BaseModel include AttrJson::Record attr_json_config(default_container_attribute: "base_data") end class DerivedModel < BaseModel # Inherits base_data as default # Override for this class only attr_json_config(default_container_attribute: "derived_data") BaseModel.attr_json_config.default_container_attribute # => "base_data" DerivedModel.attr_json_config.default_container_attribute # => "derived_data" end ``` -------------------------------- ### Create and Initialize MyModel Instance Source: https://github.com/jrochkind/attr_json/blob/master/README.md Demonstrates creating a new MyModel instance and assigning values to its attr_json attributes, including a nested embedded model. ```ruby model = MyModel.create!( my_integer: 101, my_datetime: DateTime.new(2001,2,3,4,5,6), embedded_lang_and_val: LangAndValue.new(value: "a sentance in default language english") ) ``` -------------------------------- ### Define Attributes with Options Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/index.md Demonstrates how to define attributes with various options like making them arrays, setting defaults, specifying JSON keys, and enabling nested attributes. ```ruby attr_json :title, :string attr_json :tags, :string, array: true attr_json :author, Author.to_type, accepts_nested_attributes: true attr_json :metadata, :string, store_key: "__meta", default: "" ``` -------------------------------- ### Get Attribute Type Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Retrieve the ActiveModel::Type::Value instance associated with the attribute. ```ruby attr_reader :type ``` ```ruby definition.type # => # definition.type.class # => ActiveModel::Type::String ``` -------------------------------- ### Merge Configuration from Base and Derived Classes Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/configuration.md Demonstrates how configuration settings merge between a base class and a derived class. New configurations are merged with existing ones, applying settings from both. ```ruby class Base include AttrJson::Record attr_json_config(default_container_attribute: "data") end class Derived < Base # Merges with parent config attr_json_config(default_accepts_nested_attributes: false) # Both settings now apply Derived.attr_json_config.default_container_attribute # => "data" Derived.attr_json_config.default_accepts_nested_attributes # => false end ``` -------------------------------- ### Initialize Model Instance Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Model.md Creates a new model instance with the provided attributes. Use this to instantiate objects with initial data. ```ruby contact = Contact.new(name: "John", email: "john@example.com") ``` -------------------------------- ### Initialize and Save Array of Nested Models Source: https://github.com/jrochkind/attr_json/blob/master/README.md Demonstrates initializing a `MyModel` with an array of `LangAndValue` hashes and saving it. Shows how the array of nested models is stored and retrieved. ```ruby # Arrays too, yup m = MyModel.new(lang_and_value_array: [{ lang: 'fr', value: "S'il vous plaît"}, { lang: 'en', value: "Hey there" }]) m.lang_and_value_array # => [#"fr", "value"=>"S'il vous plaît"}>, #"en", "value"=>"Hey there"}>] m.save! m.attr_jsons_before_type_cast # => string containing: {"lang_and_value_array":[{"lang":"fr","value":"S'il vous plaît"},{"lang":"en","value":"Hey there"}]} ``` -------------------------------- ### Create Articles Table Migration Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/configuration.md Sets up a database table with standard columns and JSONB columns for attr_json attributes. Includes optional GIN indexes for efficient JSONB queries. ```ruby class CreateArticles < ActiveRecord::Migration[6.0] def change create_table :articles do |t| # Standard columns t.string :title t.text :content # JSON columns for attr_json t.jsonb :metadata t.jsonb :seo_data t.timestamps end # Indexes for jsonb queries (optional but recommended) add_index :articles, :metadata, using: :gin add_index :articles, :seo_data, using: :gin end end ``` -------------------------------- ### Get Attribute Type by Name Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Retrieves the ActiveModel::Type::Value instance associated with a given attribute name. ```ruby def type_for_attribute(key) ``` ```ruby Article.attr_json_registry.type_for_attribute(:title) # => # ``` -------------------------------- ### Define Product Attributes with Primitive Types Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/types.md Demonstrates defining attributes for a Product model using various primitive types like string, integer, decimal, boolean, and datetime. Shows how input values are cast to the defined types. ```ruby class Product include AttrJson::Record attr_json :name, :string attr_json :quantity, :integer attr_json :price, :decimal attr_json :available, :boolean attr_json :created_at, :datetime end product = Product.new( name: "Widget", quantity: "42", # Cast to 42 price: 19.99, # Cast to BigDecimal available: "true", # Cast to true created_at: "2024-01-15" # Cast to Time ) ``` -------------------------------- ### Configuring Unknown Key Handling (:allow) Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/configuration.md Allows unknown attributes and serializes them to JSON. ```ruby class LegacyAddress include AttrJson::Model attr_json_config(unknown_key: :allow) attr_json :street, :string end address = LegacyAddress.new(street: "123 Main", legacy_field: "value") address.attributes # => {"street"=>"123 Main", "legacy_field"=>"value"} address.serializable_hash # => {"street"=>"123 Main", "legacy_field"=>"value"} ``` -------------------------------- ### Initialize and Save Nested Model Object Source: https://github.com/jrochkind/attr_json/blob/master/README.md Demonstrates initializing a `MyModel` with a `LangAndValue` model object and saving it. Shows how to access the nested model after initialization and retrieval. ```ruby # Set with a model object, in initializer or writer m = MyModel.new(lang_and_value: LangAndValue.new(lang: "fr", value: "S'il vous plaît")) m.lang_and_value = LangAndValue.new(lang: "es", value: "hola") m.lang_and_value # => #"es", "value"=>"hola"}> m.save! m.attr_jsons_before_type_cast # => string containing: {"lang_and_value":{"lang":"es","value":"hola"}} ``` -------------------------------- ### Get Type for Attribute Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Model.md Retrieves the type object for a given attribute name. Primarily used for compatibility with form builders. ```ruby def type_for_attribute(attr_name) ``` -------------------------------- ### Define Embedded AttrJson Model Source: https://github.com/jrochkind/attr_json/blob/master/README.md An example of an embedded model that includes AttrJson::Model, defining typed attributes for language and value. ```ruby class LangAndValue include AttrJson::Model attr_json :lang, :string, default: "en" attr_json :value, :string end ``` -------------------------------- ### Get Registered Attribute Names Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Model.md Retrieves an array of all registered attribute names for a model class. Useful for introspection or iterating over attributes. ```ruby Contact.attribute_names # => [:name, :email, :phone_numbers] ``` -------------------------------- ### Initialize and Manipulate Model with Array of Nested Models Source: https://github.com/jrochkind/attr_json/blob/master/README.md Shows how to initialize a `MyModel` with a hash for `my_labels`, assign arrays of nested models, modify them, and save the changes. ```ruby m = MyModel.new m.my_labels = {} m.my_labels # => # m.my_labels.hello = [{lang: 'en', value: 'hello'}, {lang: 'es', value: 'hola'}] m.my_labels # => #[#"en", "value"=>"hello"}>, #"es", "value"=>"hola"}>]}> m.my_labels.hello.find { |l| l.lang == "en" }.value = "Howdy" m.save! m.attr_jsons # => {"my_labels"=>#[#"en", "value"=>"Howdy"}>, #"es", "value"=>"hola"}>]}>} m.attr_jsons_before_type_cast ``` -------------------------------- ### Get Container Attribute Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Retrieve the name of the JSON column this attribute serializes to. This is only applicable for Record types; it will be nil for Model types. ```ruby attr_reader :container_attribute ``` ```ruby definition.container_attribute # => "json_attributes" or "metadata" ``` -------------------------------- ### Configuring Unknown Key Handling (:strip) Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/configuration.md Sets unknown key handling to silently ignore attributes not registered with attr_json. ```ruby class FlexibleAddress include AttrJson::Model attr_json_config(unknown_key: :strip) attr_json :street, :string end address = FlexibleAddress.new(street: "123 Main", unknown: "value") address.attributes # => {"street"=>"123 Main"} (unknown dropped) ``` -------------------------------- ### Get Attribute Types Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Model.md Retrieves a hash mapping attribute names to their corresponding type objects. Useful for understanding how attributes are stored and validated. ```ruby Contact.attribute_types # => {"name" => #, "email" => #, ...} ``` -------------------------------- ### Ignoring unknown attributes with :strip Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/errors.md Demonstrates configuring a model to silently ignore unknown attributes during initialization. ```ruby # Fix with config class FlexibleContact include AttrJson::Model attr_json_config(unknown_key: :strip) attr_json :name, :string end contact = FlexibleContact.new(name: "John", unknown: "value") contact.attributes # => {"name"=>"John"} (unknown dropped) ``` -------------------------------- ### Get Store Key Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Retrieve the JSON object key used for this attribute. This may differ from the attribute's name if a custom `store_key` was specified. ```ruby def store_key end ``` ```ruby definition.store_key # => "heading" ``` -------------------------------- ### Configuring Unknown Key Handling (:raise) Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/configuration.md Sets unknown key handling to raise an error when an unregistered attribute is set. ```ruby class StrictAddress include AttrJson::Model attr_json_config(unknown_key: :raise) attr_json :street, :string attr_json :city, :string end address = StrictAddress.new(street: "123 Main", unknown: "value") # => ActiveModel::UnknownAttributeError: unknown attribute 'unknown' ``` -------------------------------- ### to_type Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Model.md Get an ActiveModel::Type representing this model class. This is useful for using the model as a type for attributes in other `AttrJson::Record` or nested `AttrJson::Model` definitions. ```APIDOC ## to_type ### Description Get an ActiveModel::Type representing this model class, for use as an attribute type in Record or nested Model definitions. ### Method Class Method ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body * **strip_nils** (Symbol/Boolean) - Default: `:safely` - How to handle nil values in serialization. ### Returns `AttrJson::Type::Model` - Type instance ### strip_nils behavior: * `true` - Always strip nil values (default gets reapplied on deserialization) * `false` - Keep nil values in serialized output * `:safely` (default) - Strip nils only when attribute has no default ### Example: ```ruby class Author include AttrJson::Model attr_json :name, :string attr_json :bio, :string end class Book include AttrJson::Record # Use Author as a type attr_json :author, Author.to_type attr_json :contributors, Author.to_type, array: true end book = Book.new(author: { name: "Jane Doe" }) book.author # => #"Jane Doe"}> ``` ``` -------------------------------- ### Initialize and Save Nested Model with Hash Source: https://github.com/jrochkind/attr_json/blob/master/README.md Shows how to initialize and update a `MyModel` using a hash for the nested `lang_and_value` attribute. The hash is automatically cast to the `LangAndValue` model. ```ruby # Or with a hash, no problem. m = MyModel.new(lang_and_value: { lang: 'fr', value: "S'il vous plaît"}) m.lang_and_value = { lang: 'en', value: "Hey there" } m.save! m.attr_jsons_before_type_cast # => string containing: {"lang_and_value":{"lang":"en","value":"Hey there"}} found = MyModel.find(m.id) m.lang_and_value # => #"en", "value"=>"Hey there"}> ``` -------------------------------- ### ArgumentError: Missing :mode for Config initialization Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/errors.md Raised when initializing AttrJson::Config without providing the required :mode argument. ```ruby AttrJson::Config.new # Missing :mode ``` -------------------------------- ### Initialize Polymorphic Model Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/types.md Instantiate AttrJson::Type::PolymorphicModel with allowed model classes and options. ```ruby AttrJson::Type::PolymorphicModel.new(Model1, Model2, Model3, **options) ``` -------------------------------- ### Rails Form Integration with fields_for Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/NestedAttributes.md This example shows how to integrate nested `attr_json` attributes into a standard Rails form using `form_with` and `fields_for` for the nested `author` attribute. ```erb <%= form_with(model: @book) do |f| %> <%= f.text_field :title %> <%= f.fields_for :author do |author_f| %> <%= author_f.text_field :name %> <%= author_f.text_area :bio %> <% end %> <%= f.submit %> <% end %> ``` -------------------------------- ### Invalid Configuration Error Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/errors.md Demonstrates an ArgumentError raised due to an invalid configuration mode. ```ruby AttrJson::Config.new(mode: :invalid) ``` -------------------------------- ### Get Deep Duplicate of Attributes Hash Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Model.md Returns a deep duplicate of the model's attributes hash. Modifications to the returned hash do not affect the original model instance. ```ruby hash = contact.to_h hash[:name] = "Modified" contact.name # => "John" (unchanged) ``` -------------------------------- ### Form Handling with Nested Attributes Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/index.md Shows how to enable form support for nested attributes, including building new records. ```ruby class Article include AttrJson::Record include AttrJson::NestedAttributes attr_json :author, Author.to_type attr_json :reviewers, Reviewer.to_type, array: true attr_json_accepts_nested_attributes_for :author, :reviewers, limit: 5 end # Build new nested records article = Article.new article.build_author(name: "Jane") article.build_reviewer(name: "John") ``` -------------------------------- ### to_serialization_coder Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Model.md Get a serialization coder for use with Rails' `serialize` feature on entire JSON columns. This allows an `AttrJson::Model` instance to be directly serialized into a database column. ```APIDOC ## to_serialization_coder ### Description Get a serialization coder for use with Rails' `serialize` feature on entire JSON columns. ### Method Class Method ### Parameters None ### Returns `AttrJson::SerializationCoderFromType` ### Example: ```ruby class MyModel include AttrJson::Model attr_json :title, :string attr_json :content, :string end class Article < ApplicationRecord serialize :metadata, MyModel.to_serialization_coder end article = Article.new(metadata: MyModel.new(title: "Title", content: "Content")) article.save! # Entire model serialized to column ``` ``` -------------------------------- ### Default Values for Attributes Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Record.md Shows how to define default values for attributes. Defaults can be empty arrays, nil, static values, or dynamically generated using a proc. ```ruby attr_json :tags, :string, array: true # => defaults to empty array attr_json :tags, :string, array: true, default: AttrJson::AttributeDefinition::NO_DEFAULT_PROVIDED # => defaults to nil instead attr_json :status, :string, default: "draft" # => defaults to "draft" attr_json :metadata, SomeModel.to_type, default: -> { SomeModel.new } # => proc is called to get fresh default instance ``` -------------------------------- ### Cocoon Integration for Nested Array Attributes Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/NestedAttributes.md This example illustrates how to use Cocoon with `attr_json` and nested attributes to dynamically add and remove items from an array of nested models, specifically for the `contributors` attribute. ```erb <%= form_with(model: @book) do |f| %> <%= f.text_field :title %>
<%= f.simple_fields_for :contributors do |contributor_f| %> <%= render 'contributor_form', f: contributor_f %> <% end %> <%= link_to_add_association 'Add contributor', f, :contributors %>
<%= f.submit %> <% end %> **_contributor_form.html.erb:** ```erb
<%= f.input :name %> <%= f.input :bio %> <%= link_to_remove_association 'Remove', f %>
``` ``` -------------------------------- ### Configuring Default Container Attribute Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/configuration.md Sets the default column for attribute serialization and demonstrates attribute placement. ```ruby class Article include AttrJson::Record attr_json_config(default_container_attribute: "metadata") attr_json :title, :string # Goes to metadata column attr_json :view_count, :integer # Goes to metadata column attr_json :seo_data, :string, container_attribute: "seo" # Goes to seo column end # Migration create_table :articles do |t| t.jsonb :metadata t.jsonb :seo end ``` -------------------------------- ### Configuration Class Constants Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/configuration.md Defines allowed keys for record and model configurations. ```ruby class Config RECORD_ALLOWED_KEYS = %i{ default_container_attribute default_accepts_nested_attributes } MODEL_ALLOWED_KEYS = %i{ unknown_key bad_cast time_zone_aware_attributes } end ``` -------------------------------- ### Configure Article Model with JSONB Columns Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/configuration.md Configures the Article model to use specific JSONB columns for its attributes, including setting default container attributes and nested attribute handling. Defines core metadata and SEO data attributes. ```ruby class Article include AttrJson::Record include AttrJson::Record::QueryScopes attr_json_config( default_container_attribute: "metadata", default_accepts_nested_attributes: true ) # Core metadata attr_json :draft, :boolean, default: true attr_json :view_count, :integer, default: 0 attr_json :tags, :string, array: true # SEO data (different column) attr_json :seo_title, :string, container_attribute: "seo_data" attr_json :seo_description, :string, container_attribute: "seo_data" attr_json :keywords, :string, array: true, container_attribute: "seo_data" end ``` -------------------------------- ### Define Array Attributes Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/types.md Shows how to define attributes that should hold arrays of a specific type. Defaults to an empty array if not provided. ```ruby attr_json :tags, :string, array: true # => Returns Array, defaults to [] attr_json :scores, :integer, array: true # => Returns Array, defaults to [] attr_json :authors, Author.to_type, array: true # => Returns Array, defaults to [] ``` -------------------------------- ### Define Nested Attributes with attr_json_accepts_nested_attributes_for Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/NestedAttributes.md Use `attr_json_accepts_nested_attributes_for` to enable nested attribute assignment and form builder integration for model-type attributes. This example demonstrates setting up nested attributes for a single author and an array of contributors. ```ruby class Author include AttrJson::Model attr_json :name, :string attr_json :bio, :string validates :name, presence: true end class Book include AttrJson::Record include AttrJson::NestedAttributes attr_json :title, :string attr_json :author, Author.to_type attr_json :contributors, Author.to_type, array: true # Enable nested attributes attr_json_accepts_nested_attributes_for :author, :contributors end book = Book.new built_author = book.build_author(name: "Jane Doe") # book.author => Author instance ``` -------------------------------- ### Configure Model Type with strip_nils Option Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/types.md Illustrates configuring a model type with the `strip_nils` option to control how nil values are handled during serialization. The `:safely` option is the default. ```ruby Author.to_type(strip_nils: :safely) ``` -------------------------------- ### Model Configuration for AttrJson Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/index.md Shows how to configure an ActiveRecord model to use AttrJson, including setting a default container attribute and defining attributes. ```ruby class Article < ApplicationRecord include AttrJson::Record include AttrJson::Record::QueryScopes include AttrJson::NestedAttributes attr_json_config( default_container_attribute: :json_attributes ) attr_json :title, :string attr_json :status, :string, default: "draft" # ... end ``` -------------------------------- ### Get ActiveModel::Type for Nested Models Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Model.md Obtain an ActiveModel::Type instance for a model class, useful for defining nested attributes in other AttrJson::Record or Model definitions. Allows configuration for stripping nil values during serialization. ```ruby class Author include AttrJson::Model attr_json :name, :string attr_json :bio, :string end class Book include AttrJson::Record # Use Author as a type attr_json :author, Author.to_type attr_json :contributors, Author.to_type, array: true end book = Book.new(author: { name: "Jane Doe" }) book.author # => #"Jane Doe"}> ``` -------------------------------- ### Create JSONB Column Migration Source: https://github.com/jrochkind/attr_json/blob/master/README.md Defines a migration to create a table with a jsonb column for storing JSON attributes. Includes an optional GIN index for efficient querying. ```ruby class CreatMyModels < ActiveRecord::Migration[5.0] def change create_table :my_models do |t| t.jsonb :json_attributes end # If you plan to do any querying with jsonb_contains below.. add_index :my_models, :json_attributes, using: :gin end end ``` -------------------------------- ### Fetch Attribute Definition with Default Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Fetches an attribute definition, providing an optional default value or block to execute if the key is not found. ```ruby def fetch(key, *args, &block) ``` ```ruby Article.attr_json_registry.fetch(:title) Article.attr_json_registry.fetch(:missing, nil) Article.attr_json_registry.fetch(:missing) { |key| "default for #{key}" } ``` -------------------------------- ### Configure Model Class Options for Key Handling and Casting Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/index.md Configure `AttrJson::Model` classes using `attr_json_config` to control how unknown keys are handled, how bad casts are treated, and whether attributes are time zone aware. ```ruby class Author include AttrJson::Model attr_json_config( unknown_key: :strip, bad_cast: :as_nil, time_zone_aware_attributes: true ) end ``` -------------------------------- ### Registry Methods Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Methods for interacting with the attribute registry. ```APIDOC ## [] ### Description Get definition by attribute name. ### Example ```ruby Article.attr_json_registry[:title] # => AttributeDefinition Article.attr_json_registry["title"] # => AttributeDefinition ``` ## fetch ### Description Get definition with optional default or block. ### Example ```ruby Article.attr_json_registry.fetch(:title) Article.attr_json_registry.fetch(:missing, nil) Article.attr_json_registry.fetch(:missing) { |key| "default for #{key}" } ``` ## has_attribute? ### Description Check if attribute is registered. ### Example ```ruby Article.attr_json_registry.has_attribute?(:title) # => true Article.attr_json_registry.has_attribute?(:missing) # => false ``` ## definitions ### Description Get array of all AttributeDefinition objects. ### Returns Array ### Example ```ruby Article.attr_json_registry.definitions.map(&:name) # => [:title, :created_at, :view_count] ``` ## attribute_names ### Description Get array of all attribute names. ### Returns Array ### Example ```ruby Article.attr_json_registry.attribute_names # => [:title, :created_at, :view_count] ``` ## type_for_attribute ### Description Get the type for an attribute. ### Returns ActiveModel::Type::Value ### Example ```ruby Article.attr_json_registry.type_for_attribute(:title) # => # ``` ## store_key_lookup ### Description Look up a definition by container and store key. ### Returns AttributeDefinition or nil ### Example ```ruby # Find attribute with store_key "heading" in "json_attributes" Article.attr_json_registry.store_key_lookup("json_attributes", "heading") # => AttributeDefinition for attribute with that store_key ``` ``` -------------------------------- ### Catching AttrJson Specific Errors Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/errors.md Illustrates how to use a begin-rescue block to catch various AttrJson-related exceptions, including type casting and configuration errors. ```ruby begin book = Book.new(author: "invalid") rescue AttrJson::Type::Model::BadCast => e puts "Invalid author: #{e.message}" rescue AttrJson::Type::PolymorphicModel::TypeError => e puts "Type mismatch: #{e.message}" rescue ArgumentError => e puts "Configuration error: #{e.message}" end ``` -------------------------------- ### List All Attributes with Their Types Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Iterates through all registered attribute definitions and prints their names along with their corresponding ActiveModel::Type class names. ```ruby Article.attr_json_registry.definitions.each do |defn| puts "#{defn.name}: #{defn.type.class.name}" end ``` -------------------------------- ### Class Methods Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/AttributeDefinition.md Static methods available on the AttributeDefinition class. ```APIDOC ## lookup_type ### Description Convert a type symbol or type object to an ActiveModel::Type::Value instance. ### Parameters #### type - **type** (Symbol/ActiveModel::Type::Value) - Type to lookup ### Returns ActiveModel::Type::Value instance ### Raises `ArgumentError` for invalid types ### Example ```ruby AttrJson::AttributeDefinition.lookup_type(:string) # => # AttrJson::AttributeDefinition.lookup_type(:datetime, precision: 3) # => # ``` ## single_model_type? ### Description Check if a type is a nested model type (static method). ### Parameters #### arg_type - **arg_type** (ActiveModel::Type::Value) - Type to check ### Returns Boolean ### Example ```ruby author_type = Author.to_type AttrJson::AttributeDefinition.single_model_type?(author_type) # => true ``` ``` -------------------------------- ### Configure Class-Wide JSON Attributes Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/Record.md Access or set class-wide configuration for JSON attributes. Use this to define default settings like the container attribute name. ```ruby class MyModel < ActiveRecord::Base include AttrJson::Record # Set configuration for the class attr_json_config(default_container_attribute: :data) # Access current configuration config = MyModel.attr_json_config puts config.default_container_attribute # => "data" end ``` -------------------------------- ### PostgreSQL Querying with AttrJson Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/index.md Illustrates how to use AttrJson's query scopes for querying JSONB columns in PostgreSQL, including simple matching, nested models, and array containment. ```ruby class Article include AttrJson::Record include AttrJson::Record::QueryScopes attr_json :status, :string attr_json :created_at, :datetime attr_json :tags, :string, array: true end # Simple matching Article.jsonb_contains(status: "published") # Nested models Article.jsonb_contains("author.name" => "Jane") # Arrays Article.jsonb_contains(tags: ["ruby", "rails"]) # Chainable Article .jsonb_contains(status: "published") .where(created_at: 1.week.ago..) .order(view_count: :desc) ``` -------------------------------- ### Dynamic Build Methods Source: https://github.com/jrochkind/attr_json/blob/master/_autodocs/api-reference/NestedAttributes.md When `define_build_method: true` is set for `attr_json_accepts_nested_attributes_for`, builder methods like `build_#{singular_name}` are created to instantiate and associate nested models. ```APIDOC ## Dynamic Build Methods When `define_build_method: true`, builder methods are created: ```ruby class Book include AttrJson::Record include AttrJson::NestedAttributes attr_json :author, Author.to_type attr_json :contributors, Author.to_type, array: true attr_json_accepts_nested_attributes_for :author, :contributors end book = Book.new # Single model - creates and returns the model author = book.build_author(name: "Jane Doe") book.author == author # => true # Array - adds to array and returns the added model contributor = book.build_contributor(name: "John Doe") book.contributors.last == contributor # => true ``` ```