Skip to content

Instantly share code, notes, and snippets.

@alpaca-tc
Created May 23, 2021 13:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alpaca-tc/80b9fd778b38695bae639006a803ecbf to your computer and use it in GitHub Desktop.
Save alpaca-tc/80b9fd778b38695bae639006a803ecbf to your computer and use it in GitHub Desktop.
渡された値でクラスを初期化する型
# Usage
# attribute :wrapped_type, klass: UserModel, array: true
class WrappedType < ActiveModel::Type::Value
KEYWORD_PARAMETERS = %i[keyreq key].freeze
private_constant(:KEYWORD_PARAMETERS)
# @param [Class, String] klass 変換したいクラス
# @param [Boolean] array 配列で変換するかどうか
def initialize(klass:, array: false)
super()
@wrap_klass = klass
@array = array
end
# @param [any, Array, Hash] value 変換したい値。Hash の場合は `value { '0' => { }, '1' => { } }` 形式の、Controllerから渡ってくるようなHash
def cast(value)
if @array
cast_array(value)
else
cast_value(value)
end
end
private
def cast_value(value)
_cast(value) unless value.nil?
end
def cast_array(values)
if values.blank?
[]
elsif values.respond_to?(:keys) && values.keys.all? { |key| key.match?(/^\d+$/) }
values.values.map { |value| _cast(value) }
elsif values.respond_to?(:each)
values.map { |value| _cast(value) }
else
raise ArgumentError, "value is not enumerable. #{values}"
end
end
def _cast(value)
if value.is_a?(klass)
value
elsif keyword_parameters?
klass.new(**value)
else
klass.new(value)
end
end
def keyword_parameters?
if defined?(@keyword_parameters)
@keyword_parameters
else
# 引数が全てキーワード引数かどうか
@keyword_parameters = klass.instance_method(:initialize).parameters.all? { |(key, _)| KEYWORD_PARAMETERS.include?(key) }
end
end
def klass
@klass ||= if @wrap_klass.respond_to?(:constantize)
@wrap_klass.constantize
else
@wrap_klass
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment