Module: JSONAPIonify::Api::Resource::Definitions::Actions

Defined in:
lib/jsonapionify/api/resource/definitions/actions.rb

Constant Summary collapse

ActionNotFound =
Class.new StandardError
DEFAULT_SAVE_COMMIT =
proc do |instance:, request_attributes:, request_relationships:|
  # Assign the attributes
  request_attributes.each do |key, value|
    instance.send "#{key}=", value
  end

  # Assign the relationships
  request_relationships.each do |key, value|
    instance.send "#{key}=", value
  end

  # Save the instance
  instance.save if instance.respond_to? :save
end
DEFAULT_DELETE_COMMIT =
proc do |instance:|
  instance.respond_to?(:destroy) ? instance.destroy : instance.respond_to?(:delete) ? instance.delete : nil
end
INSTANCE_RESPONSE =
proc do |context, instance:, response_object:, builder: nil|
  response_object[:data] = build_resource(context: context, instance: instance, &builder)
  response_object.to_json
end
CREATE_RESPONSE =
proc do |context, instance:, response_object:, builder: nil, response_headers:|
  instance_exec(context, instance: instance, response_object: response_object, builder: builder, &INSTANCE_RESPONSE).tap do
    response_headers['Location'] = response_object[:data][:links][:self]
  end
end
COLLECTION_RESPONSE =
proc do |context, response_collection:, links:, response_object:, builder: nil, nested_request: false|
  response_object[:data] = build_resource_collection(
    context: context,
    collection: response_collection,
    include_cursors: (links.keys & [:first, :last, :next, :prev]).present?,
    &builder
  )
  response_object.to_json unless nested_request
end

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(klass) ⇒ Object



49
50
51
52
53
54
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 49

def self.extended(klass)
  klass.class_eval do
    extend JSONAPIonify::InheritedAttributes
    inherited_array_attribute :action_definitions
  end
end

Instance Method Details

#action(name) ⇒ Object



151
152
153
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 151

def action(name)
  actions.find { |action| action.name == name }
end

#actionsObject



155
156
157
158
159
160
161
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 155

def actions
  return [] if action_definitions.blank?
  action_definitions.select do |action|
    action.only_associated == false ||
      (respond_to?(:rel) && action.only_associated == true)
  end
end

#call_action(name, request, **context_overrides) ⇒ Object



147
148
149
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 147

def call_action(name, request, **context_overrides)
  action(name).call(self, request, **context_overrides)
end

#create(**options, &block) ⇒ Object



62
63
64
65
66
67
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 62

def create(**options, &block)
  block ||= DEFAULT_SAVE_COMMIT
  define_action(:create, 'POST', '', **options, cacheable: false, example_input: :resource, &block).tap do |action|
    action.response(status: 201, &CREATE_RESPONSE)
  end
end

#define_action(name, *args, **options, &block) ⇒ Object



99
100
101
102
103
104
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 99

def define_action(name, *args, **options, &block)
  Action.new(name, *args, **options, &block).tap do |new_action|
    action_definitions.delete new_action
    action_definitions << new_action
  end
end

#delete(**options, &block) ⇒ Object



82
83
84
85
86
87
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 82

def delete(**options, &block)
  block ||= DEFAULT_DELETE_COMMIT
  define_action(:delete, 'DELETE', '/:id', **options, cacheable: false, &block).tap do |action|
    action.response(status: 204)
  end
end

#documented_actionsObject



163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 163

def documented_actions
  api.eager_load
  relationships = descendants.select { |descendant| descendant.respond_to? :rel }
  rels          = relationships.each_with_object([]) do |rel, ary|
    rel.actions.each do |action|
      ary << [action, "#{rel.rel.owner.type}/:id", [rel, rel.rel.name, false, "#{action.name} #{rel.rel.owner.type.singularize.possessive} #{rel.rel.name}"]]
    end
  end
  actions.map do |action|
    [action, '', [self, type, true, "#{action.name} #{type}"]]
  end + rels
end

#find_supported_action(request) ⇒ Object



106
107
108
109
110
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 106

def find_supported_action(request)
  actions.find do |action|
    action.supports?(request, base_path, path_name, supports_path?)
  end
end

#find_supported_relationship(request) ⇒ Object



134
135
136
137
138
139
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 134

def find_supported_relationship(request)
  relationship_definitions.find do |rel|
    relationship = self.relationship(rel.name)
    relationship != self && relationship.path_actions(request).present?
  end
end

#list(**options, &block) ⇒ Object



56
57
58
59
60
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 56

def list(**options, &block)
  define_action(:list, 'GET', **options, cacheable: true, &block).tap do |action|
    action.response(status: 200, &COLLECTION_RESPONSE)
  end
end

#no_action_response(request) ⇒ Object



112
113
114
115
116
117
118
119
120
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 112

def no_action_response(request)
  if request_method_actions(request).present?
    Action.error :unsupported_media_type
  elsif self.path_actions(request).present?
    Action.error :forbidden
  else
    Action.error :not_found
  end
end

#path_actions(request) ⇒ Object



122
123
124
125
126
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 122

def path_actions(request)
  actions.select do |action|
    action.supports_path?(request, base_path, path_name, supports_path?)
  end
end

#process(request) ⇒ Object



89
90
91
92
93
94
95
96
97
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 89

def process(request)
  if (action = find_supported_action(request))
    action.call(self, request)
  elsif (rel = find_supported_relationship(request))
    relationship(rel.name).process(request)
  else
    no_action_response(request).call(self, request)
  end
end

#read(**options, &block) ⇒ Object



69
70
71
72
73
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 69

def read(**options, &block)
  define_action(:read, 'GET', '/:id', **options, cacheable: true, &block).tap do |action|
    action.response(status: 200, &INSTANCE_RESPONSE)
  end
end

#remove_action(*names) ⇒ Object



141
142
143
144
145
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 141

def remove_action(*names)
  action_definitions.delete_if do |action_definition|
    names.include? action_definition.name
  end
end

#request_method_actions(request) ⇒ Object



128
129
130
131
132
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 128

def request_method_actions(request)
  path_actions(request).select do |action|
    action.supports_request_method?(request)
  end
end

#update(**options, &block) ⇒ Object



75
76
77
78
79
80
# File 'lib/jsonapionify/api/resource/definitions/actions.rb', line 75

def update(**options, &block)
  block ||= DEFAULT_SAVE_COMMIT
  define_action(:update, 'PATCH', '/:id', **options, cacheable: false, example_input: :resource, &block).tap do |action|
    action.response(status: 200, &INSTANCE_RESPONSE)
  end
end