mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-04 18:48:01 +08:00
83 lines
2 KiB
Ruby
83 lines
2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Service::ContractBase
|
|
include ActiveModel::API
|
|
include ActiveModel::Attributes
|
|
include ActiveModel::AttributeMethods
|
|
include ActiveModel::Validations::Callbacks
|
|
|
|
delegate :slice, :merge, :except, to: :to_hash
|
|
|
|
class << self
|
|
def attribute(name, cast_type = nil, **options, &block)
|
|
return super(name, cast_type, **options) unless block_given?
|
|
|
|
nested_contract_class =
|
|
Class.new(Service::ContractBase) do
|
|
define_singleton_method(:name) { "#{name}_contract".classify }
|
|
class_eval(&block)
|
|
end
|
|
super(
|
|
name,
|
|
Service::NestedContractType.new(
|
|
contract_class: nested_contract_class,
|
|
nested_type: cast_type || :hash,
|
|
),
|
|
**options,
|
|
)
|
|
end
|
|
end
|
|
|
|
def initialize(*args, options: nil, **kwargs)
|
|
@__options__ = options
|
|
kwargs.deep_symbolize_keys!.slice!(*self.class.attribute_names.map(&:to_sym))
|
|
super(*args, **kwargs)
|
|
end
|
|
|
|
def options
|
|
@__options__
|
|
end
|
|
|
|
def to_hash
|
|
attributes.symbolize_keys.deep_transform_values do
|
|
it.is_a?(Service::ContractBase) ? it.to_hash : it
|
|
end
|
|
end
|
|
|
|
def raw_attributes
|
|
@attributes.values_before_type_cast
|
|
end
|
|
|
|
def valid?(context = nil)
|
|
[super, nested_attributes_valid?].all?
|
|
end
|
|
|
|
private
|
|
|
|
def nested_attributes_valid?
|
|
nested_attributes.map(&method(:validate_nested)).all?
|
|
end
|
|
|
|
def nested_attributes
|
|
@attributes.each_value.select { it.type.is_a?(Service::NestedContractType) && it.value }
|
|
end
|
|
|
|
def validate_nested(attribute)
|
|
Array
|
|
.wrap(attribute.value)
|
|
.map
|
|
.with_index do |contract, index|
|
|
next true if contract.valid?
|
|
import_nested_errors(contract, attribute, index)
|
|
false
|
|
end
|
|
.all?
|
|
end
|
|
|
|
def import_nested_errors(contract, attribute, index)
|
|
array_index = "[#{index}]" if attribute.value.is_a?(Array)
|
|
contract.errors.each do |error|
|
|
errors.import(error, attribute: :"#{attribute.name}#{array_index}.#{error.attribute}")
|
|
end
|
|
end
|
|
end
|