lib/jsonapionify/api/attribute.rb in jsonapionify-0.9.0 vs lib/jsonapionify/api/attribute.rb in jsonapionify-0.9.1
- old
+ new
@@ -1,46 +1,123 @@
require 'unstrict_proc'
module JSONAPIonify::Api
class Attribute
using UnstrictProc
- attr_reader :name, :type, :description, :read, :write, :required
+ attr_reader :name, :type, :description, :read, :write, :required, :block
- def initialize(name, type, description, read: true, write: true, required: false, example: nil)
- raise ArgumentError, 'required attributes must be writable' if required && !write
+ def initialize(
+ name,
+ type,
+ description,
+ read: true,
+ write: true,
+ required: false,
+ example: nil,
+ &block
+ )
unless type.is_a? JSONAPIonify::Types::BaseType
raise TypeError, "#{type} is not a valid JSON type"
end
- @name = name
- @type = type
- @description = description
- @example = example
- @read = read
- @write = write
- @required = write ? required : false
+
+ @name = name.to_sym
+ @type = type&.freeze
+ @description = description&.freeze
+ @example = example&.freeze
+ @read = read&.freeze
+ @write = write&.freeze
+ @required = required&.freeze
+ @block = block&.freeze
+ @writeable_actions = write
+ @readable_actions = read
+
+ freeze
end
def ==(other)
self.class == other.class &&
self.name == other.name
end
- def options_json
- {
- name: name,
- required: required
- }
+ def supports_read_for_action?(action_name, context)
+ case (setting = @readable_actions)
+ when TrueClass, FalseClass
+ setting
+ when Hash
+ !!JSONAPIonify::Continuation.new(setting).check(action_name, context) { true }
+ when Array
+ setting.map(&:to_sym).include? action_name
+ when Symbol, String
+ setting.to_sym === action_name
+ else
+ false
+ end
end
- def required?
- !!@required
+ def supports_write_for_action?(action_name, context)
+ action = context.resource.class.actions.find { |a| a.name == action_name }
+ return false unless %{POST PUT PATCH}.include? action.request_method
+ case (setting = @writeable_actions)
+ when TrueClass, FalseClass
+ setting
+ when Hash
+ !!JSONAPIonify::Continuation.new(setting).check(action_name, context) { true }
+ when Array
+ setting.map(&:to_sym).include? action_name
+ when Symbol, String
+ setting.to_sym === action_name
+ else
+ false
+ end
end
- def optional?
- !required?
+ def resolve(instance, context, example_id: nil)
+ if context.respond_to?(:_is_example_) && context._is_example_ == true
+ return example(example_id)
+ end
+ block = self.block || proc { |attr, i| i.send attr }
+ type.dump block.unstrict.call(self.name, instance, context)
+ rescue JSONAPIonify::Types::DumpError => ex
+ error_block =
+ context.resource.class.error_definitions[:attribute_type_error]
+ context.errors.evaluate(
+ name,
+ error_block: error_block,
+ backtrace: ex.backtrace,
+ runtime_block: proc {
+ detail ex.message
+ }
+ )
+ rescue JSONAPIonify::Types::NotNullError => ex
+ error_block =
+ context.resource.class.error_definitions[:attribute_cannot_be_null]
+ context.errors.evaluate(
+ name,
+ error_block: error_block,
+ backtrace: ex.backtrace,
+ runtime_block: proc {}
+ )
+ nil
end
+ def required_for_action?(action_name, context)
+ supports_write_for_action?(action_name, context) &&
+ (required === true || Array.wrap(required).include?(action_name))
+ end
+
+ def options_json_for_action(action_name, context)
+ {
+ name: @name,
+ type: @type.to_s,
+ description: JSONAPIonify::Documentation.onelinify_markdown(description),
+ example: example(context.resource.class.generate_id)
+ }.tap do |opts|
+ opts[:not_null] = true if @type.not_null?
+ opts[:required] = true if required_for_action?(action_name, context)
+ end
+ end
+
def read?
!!@read
end
def write?
@@ -60,10 +137,10 @@
def documentation_object
OpenStruct.new(
name: name,
type: type.name,
- required: required?,
+ required: Array.wrap(required).join(', '),
description: JSONAPIonify::Documentation.render_markdown(description),
allow: allow
)
end