module Virtus # Attribute objects handle coercion and provide interface to hook into an # attribute set instance that's included into a class or object # # @example # # # non-strict mode # attr = Virtus::Attribute.build(Integer) # attr.coerce('1') # # => 1 # # # strict mode # attr = Virtus::Attribute.build(Integer, :strict => true) # attr.coerce('not really coercible') # # => Virtus::CoercionError: Failed to coerce "fsafa" into Integer # class Attribute extend DescendantsTracker, Options, TypeLookup include ::Equalizer.new(:type, :options) accept_options :primitive, :accessor, :default, :lazy, :strict, :required, :finalize, :nullify_blank strict false required true accessor :public finalize true nullify_blank false # @see Virtus.coerce # # @deprecated # # @api public def self.coerce(value = Undefined) Virtus.warn "#{self}.coerce is deprecated and will be removed in 1.0.0. Use Virtus.coerce instead: ##{caller.first}" return Virtus.coerce if value.equal?(Undefined) Virtus.coerce = value self end # Return type of this attribute # # @return [Axiom::Types::Type] # # @api public attr_reader :type # @api private attr_reader :primitive, :options, :default_value, :coercer # Builds an attribute instance # # @param [Class,Array,Hash,String,Symbol] type # this can be an explicit class or an object from which virtus can infer # the type # # @param [#to_hash] options # optional extra options hash # # @return [Attribute] # # @api public def self.build(type, options = {}) Builder.call(type, options) end # @api private def self.build_coercer(type, options = {}) Coercer.new(type, options.fetch(:configured_coercer) { Virtus.coercer }) end # @api private def self.build_type(definition) Axiom::Types.infer(definition.primitive) end # @api private def self.merge_options!(*) # noop end # @api private def initialize(type, options) @type = type @primitive = type.primitive @options = options @default_value = options.fetch(:default_value) @coercer = options.fetch(:coercer) end # Coerce the input into the expected type # # @example # # attr = Virtus::Attribute.build(String) # attr.coerce(:one) # => 'one' # # @param [Object] input # # @api public def coerce(input) coercer.call(input) end # Return a new attribute with the new name # # @param [Symbol] name # # @return [Attribute] # # @api public def rename(name) self.class.build(type, options.merge(:name => name)) end # Return if the given value was coerced # # @param [Object] value # # @return [Boolean] # # @api public def value_coerced?(value) coercer.success?(primitive, value) end # Return if the attribute is coercible # # @example # # attr = Virtus::Attribute.build(String, :coerce => true) # attr.coercible? # => true # # attr = Virtus::Attribute.build(String, :coerce => false) # attr.coercible? # => false # # @return [Boolean] # # @api public def coercible? kind_of?(Coercible) end # Return if the attribute has lazy default value evaluation # # @example # # attr = Virtus::Attribute.build(String, :lazy => true) # attr.lazy? # => true # # attr = Virtus::Attribute.build(String, :lazy => false) # attr.lazy? # => false # # @return [Boolean] # # @api public def lazy? kind_of?(LazyDefault) end # Return if the attribute is in the strict coercion mode # # @example # # attr = Virtus::Attribute.build(String, :strict => true) # attr.strict? # => true # # attr = Virtus::Attribute.build(String, :strict => false) # attr.strict? # => false # # @return [Boolean] # # @api public def strict? kind_of?(Strict) end # Return if the attribute is in the nullify blank coercion mode # # @example # # attr = Virtus::Attribute.build(String, :nullify_blank => true) # attr.nullify_blank? # => true # # attr = Virtus::Attribute.build(String, :nullify_blank => false) # attr.nullify_blank? # => false # # @return [Boolean] # # @api public def nullify_blank? kind_of?(NullifyBlank) end # Return if the attribute is accepts nil values as valid coercion output # # @example # # attr = Virtus::Attribute.build(String, :required => true) # attr.required? # => true # # attr = Virtus::Attribute.build(String, :required => false) # attr.required? # => false # # @return [Boolean] # # @api public def required? options[:required] end # Return if the attribute was already finalized # # @example # # attr = Virtus::Attribute.build(String, :finalize => true) # attr.finalized? # => true # # attr = Virtus::Attribute.build(String, :finalize => false) # attr.finalized? # => false # # @return [Boolean] # # @api public def finalized? frozen? end # @api private def define_accessor_methods(attribute_set) attribute_set.define_reader_method(self, name, options[:reader]) attribute_set.define_writer_method(self, "#{name}=", options[:writer]) end # @api private def finalize freeze self end end # class Attribute end # module Virtus