# frozen_string_literal: true module Composable module Core module AttributeDSL extend ActiveSupport::Concern include InheritableAttributes included do inheritable_attributes :attributes, default: Set.new inheritable_attributes :_attribute_options, default: {} end def initialize(options = {}) unless options.respond_to?(:has_key?) raise ArgumentError, "When assigning attributes, you must pass a hash" \ " as an argument, #{options.class} passed." end attributes.each do |attribute| # Try a `string` key if `symbol` key does not exist value = options.fetch(attribute.to_sym, options[attribute.to_s]) send("#{attribute}=", value) unless value.nil? end end def attributes self.class.attributes end def params attributes.each_with_object({}) do |attribute, hash| hash[attribute] = send(attribute) end end module ClassMethods def attribute(*attrs) options = attrs.extract_options! return attributes if attrs.empty? attrs.each do |attribute| _save_options_for(attribute, **options) next if attributes.include?(attribute.to_sym) _define_getter(attribute) _define_setter(attribute) _define_question_mark_method(attribute) attributes << attribute.to_sym end end private def _define_getter(attribute) define_method(attribute) do options = self.class._attribute_options.fetch(attribute.to_sym, {}) value = if instance_variable_defined?("@#{attribute}") instance_variable_get("@#{attribute}") else instance_variable_set("@#{attribute}", options[:default].deep_dup) end options[:type] ? options[:type].cast(value) : value end end def _define_setter(attribute) define_method("#{attribute}=") do |value| options = self.class._attribute_options.fetch(attribute.to_sym, {}) instance_variable_set("@#{attribute}", options[:type] ? options[:type].cast(value) : value) end end def _define_question_mark_method(attribute) define_method("#{attribute}?") do send(attribute).present? end end def _save_options_for(attribute, type: nil, default: nil, **typed_options) options = _attribute_options[attribute.to_sym] ||= {} options[:default] = default unless default.nil? options[:type] = ActiveModel::Type.lookup(type, **typed_options) unless type.nil? end end end end end