Module: AttrJson::Model
- Extended by:
- ActiveSupport::Concern
- Includes:
- ActiveModel::Model, ActiveModel::Serialization
- Defined in:
- lib/attr_json/model.rb,
lib/attr_json/model/cocoon_compat.rb,
lib/attr_json/model/nested_model_validator.rb
Overview
Includes ActiveModel::Model whether you like it or not.
Meant for use in a plain class, turns it into an ActiveModel::Model
with attr_json support. NOT for use in an ActiveRecord::Base model,
see Record
for ActiveRecord use.
Creates an ActiveModel object with typed attributes, easily serializable to json, and with a corresponding ActiveModel::Type representing the class. Meant for use as an attribute of a AttrJson::Record. Can be nested, AttrJson::Models can have attributes that are other AttrJson::Models.
You can control what happens if you set an unknown key (one that you didn't
register with attr_json
) with the config attribute attr_json_config(unknown_key:)
.
- :raise (default) raise ActiveModel::UnknownAttributeError
- :strip Ignore the unknown key and do not include it, without raising.
:allow Allow the unknown key and it's value to be in the serialized hash, and written to the database. May be useful for legacy data or columns that other software touches, to let unknown keys just flow through.
class Something include AttrJson::Model attr_json_config(unknown_key: :allow) #... end
Similarly, trying to set a Model-valued attribute with an object that
can't be cast to a Hash or Model at all will normally raise a
AttrJson::Type::Model::BadCast error, but you can set config bad_cast: :as_nil
to make it cast to nil, more like typical ActiveRecord cast.
class Something
include AttrJson::Model
attr_json_config(bad_cast: :as_nil)
#...
end
Date-type timezone conversion
By default, AttrJson::Model date/time attributes will be
ActiveRecord timezone-aware
based on settings of config.active_record.time_zone_aware_attributes
and
ActiveRecord::Base.time_zone_aware_types
.
If you'd like to override this, you can set:
attr_json_config(time_zone_aware_attributes: true)
attr_json_config(time_zone_aware_attributes: false)
attr_json_config(time_zone_aware_attributes: [:datetime, :time]) # custom list of types
ActiveRecord serialize
If you want to map a single AttrJson::Model to a json/jsonb column, you
can use ActiveRecord serialize
feature.
https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html
We provide a simple shim to give you the right API for a "coder" for AR serialize:
class ValueModel include AttrJson::Model attr_json :some_string, :string end
class SomeModel < ApplicationRecord serialize :some_json_column, ValueModel.to_serialize_coder end
Strip nils
When embedded in an attr_json
attribute, models are normally serialized with nil
values
stripped from hash where possible, for a more compact representation.
This can be set differently in the type.
attr_json :lang_and_value, LangAndValue.to_type(strip_nils: false)
See #serializable_hash docs for possible values.
Defined Under Namespace
Modules: CocoonCompat Classes: NestedModelValidator
Instance Method Summary collapse
-
#==(other_object) ⇒ Object
Two AttrJson::Model objects are equal if they are the same class AND their #attributes are equal.
-
#_destroy ⇒ Object
ActiveRecord objects have a
_destroy
, related tomarked_for_destruction?
functionality used with AR nested attributes. -
#as_json(options = nil) ⇒ Object
ActiveRecord JSON serialization will insist on calling this, instead of the specified type's #serialize, at least in some cases.
-
#assign_attributes(new_attributes) ⇒ Object
ActiveModel method, called in initialize.
-
#attribute_names ⇒ Object
like the ActiveModel::Attributes method.
- #attributes ⇒ Object
-
#freeze ⇒ Object
like ActiveModel::Attributes at https://github.com/rails/rails/blob/8015c2c2cf5c8718449677570f372ceb01318a32/activemodel/lib/active_model/attributes.rb#L120.
-
#has_attribute?(str) ⇒ Boolean
This attribute from ActiveRecord make SimpleForm happy, and able to detect type.
- #initialize(attributes = {}) ⇒ Object
- #initialize_dup(other) ⇒ Object
-
#serializable_hash(options = nil) ⇒ Object
Override from ActiveModel::Serialization to:.
-
#to_h ⇒ Object
We deep_dup on #to_h, you want attributes unduped, ask for #attributes.
-
#type_for_attribute(attr_name) ⇒ Object
This attribute from ActiveRecord makes SimpleForm happy, and able to detect type.
Instance Method Details
#==(other_object) ⇒ Object
Two AttrJson::Model objects are equal if they are the same class AND their #attributes are equal.
406 407 408 |
# File 'lib/attr_json/model.rb', line 406 def ==(other_object) other_object.class == self.class && other_object.attributes == self.attributes end |
#_destroy ⇒ Object
ActiveRecord objects have a
_destroy
, related to marked_for_destruction?
functionality used with AR nested attributes.
We don't mark for destruction, our nested attributes implementation just deletes immediately,
but having this simple method always returning false makes things work more compatibly
and smoothly with standard code for nested attributes deletion in form builders.
415 416 417 |
# File 'lib/attr_json/model.rb', line 415 def _destroy false end |
#as_json(options = nil) ⇒ Object
ActiveRecord JSON serialization will insist on calling this, instead of the specified type's #serialize, at least in some cases. So it's important we define it -- the default #as_json added by ActiveSupport will serialize all instance variables, which is not what we want.
394 395 396 |
# File 'lib/attr_json/model.rb', line 394 def as_json(=nil) serializable_hash() end |
#assign_attributes(new_attributes) ⇒ Object
ActiveModel method, called in initialize. overridden. from https://github.com/rails/rails/blob/42a16a4d6514f28e05f1c22a5f9125d194d9c7cb/activemodel/lib/active_model/attribute_assignment.rb
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 |
# File 'lib/attr_json/model.rb', line 307 def assign_attributes(new_attributes) if !new_attributes.respond_to?(:stringify_keys) raise ArgumentError, "When assigning attributes, you must pass a hash as an argument." end return if new_attributes.empty? # stringify keys just like https://github.com/rails/rails/blob/4f99a2186479d5f77460622f2c0f37708b3ec1bc/activemodel/lib/active_model/attribute_assignment.rb#L34 new_attributes.stringify_keys.each do |k, v| setter = :"#{k}=" if respond_to?(setter) public_send(setter, v) else _attr_json_write_unknown_attribute(k, v) end end end |
#attribute_names ⇒ Object
like the ActiveModel::Attributes method
337 338 339 |
# File 'lib/attr_json/model.rb', line 337 def attribute_names self.class.attribute_names end |
#attributes ⇒ Object
301 302 303 |
# File 'lib/attr_json/model.rb', line 301 def attributes @attributes ||= {} end |
#freeze ⇒ Object
like ActiveModel::Attributes at https://github.com/rails/rails/blob/8015c2c2cf5c8718449677570f372ceb01318a32/activemodel/lib/active_model/attributes.rb#L120
is not a full deep freeze
423 424 425 426 |
# File 'lib/attr_json/model.rb', line 423 def freeze attributes.freeze unless frozen? super end |
#has_attribute?(str) ⇒ Boolean
This attribute from ActiveRecord make SimpleForm happy, and able to detect type.
332 333 334 |
# File 'lib/attr_json/model.rb', line 332 def has_attribute?(str) self.class.attr_json_registry.has_attribute?(str) end |
#initialize(attributes = {}) ⇒ Object
289 290 291 292 293 |
# File 'lib/attr_json/model.rb', line 289 def initialize(attributes = {}) super fill_in_defaults! end |
#initialize_dup(other) ⇒ Object
296 297 298 299 |
# File 'lib/attr_json/model.rb', line 296 def initialize_dup(other) # :nodoc: @attributes = @attributes.deep_dup super end |
#serializable_hash(options = nil) ⇒ Object
Override from ActiveModel::Serialization to:
handle store_key settings
serialize by type to make sure any values set directly on hash still
get properly type-serialized.
custom logic for keeping nil values out of serialization to be more compact
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
# File 'lib/attr_json/model.rb', line 360 def serializable_hash(=nil) strip_nils = &.has_key?(:strip_nils) ? .delete(:strip_nils) : false unless [true, false, :safely].include?(strip_nils) raise ArgumentError, ":strip_nils must be true, false, or :safely" end super().collect do |key, value| if attribute_def = self.class.attr_json_registry[key.to_sym] key = attribute_def.store_key value = attribute_def.serialize(value) end # strip_nils handling if value.nil? && ( (strip_nils == :safely && !attribute_def&.has_default?) || strip_nils == true ) then # do not include in serializable_hash nil else [key, value] end end.compact.to_h end |
#to_h ⇒ Object
We deep_dup on #to_h, you want attributes unduped, ask for #attributes.
400 401 402 |
# File 'lib/attr_json/model.rb', line 400 def to_h attributes.deep_dup end |
#type_for_attribute(attr_name) ⇒ Object
This attribute from ActiveRecord makes SimpleForm happy, and able to detect type.
326 327 328 |
# File 'lib/attr_json/model.rb', line 326 def type_for_attribute(attr_name) self.class.attr_json_registry.type_for_attribute(attr_name) end |