lib/axiom/types/support/options.rb
# encoding: utf-8
module Axiom
module Types
# A module that adds class and instance level options
module Options
# Raised when the method is already used
class ReservedMethodError < ArgumentError; end
# Defines which options are valid for a given attribute class
#
# @example
# class MyTypes < Axiom::Types::Object
# accept_options :foo, :bar
# end
#
# @return [self]
#
# @api public
def accept_options(*new_options)
(new_options - accepted_options).each do |new_option|
assert_method_available(new_option)
define_option_method(new_option)
setup_option(new_option)
end
self
end
protected
# Set up the option in the current class and descendants
#
# @param [Symbol] new_option
# new option to be added
#
# @return [self]
#
# @api private
def setup_option(new_option)
instance_variable_set(:"@#{new_option}", nil)
accepted_options << new_option
descendants.each do |descendant|
descendant.send(__method__, new_option)
end
self
end
private
# Adds descendant to descendants array and inherits default options
#
# @param [Class] descendant
#
# @return [undefined]
#
# @api private
def inherited(descendant)
super
options.each do |option, value|
descendant.setup_option(option).public_send(option, value)
end
end
# Returns default options hash for a given attribute class
#
# @example
# Axiom::Types::String.options
# # => {:primitive => String}
#
# @return [Hash]
# a hash of default option values
#
# @api private
def options
accepted_options.each_with_object({}) do |name, options|
options[name] = public_send(name)
end
end
# Returns an array of valid options
#
# @example
# Axiom::Types::String.accepted_options
# # => [:primitive, :accessor, :reader, :writer]
#
# @return [Array]
# the array of valid option names
#
# @api private
def accepted_options
@accepted_options ||= []
end
# Assert that the option is not already defined
#
# @param [Symbol] name
#
# @return [undefined]
#
# @raise [ReservedMethodError]
# raised when the method is already defined
#
# @api private
def assert_method_available(name)
return unless respond_to?(name)
fail(
ReservedMethodError,
"method named `#{name.inspect}` is already defined"
)
end
# Adds a reader/writer method for the give option name
#
# @param [#to_s] name
#
# @return [undefined]
#
# @api private
def define_option_method(name)
ivar = :"@#{name}"
define_singleton_method(name) do |*args|
return instance_variable_get(ivar) if args.empty?
instance_variable_set(ivar, *args)
self
end
end
end # module Options
end # module Types
end # module Axiom