lib/metatron/composite_controller.rb in metatron-0.5.0 vs lib/metatron/composite_controller.rb in metatron-0.6.0

- old
+ new

@@ -2,53 +2,67 @@ module Metatron # Implementes a Metacontroller CompositeController # @see https://metacontroller.github.io/metacontroller/api/compositecontroller.html class CompositeController < Controller - options "/sync" do - headers "Access-Control-Allow-Methods" => ["POST"] - halt 200 + def initialize(env) + super + @strategy = nil end - post "/sync" do - if (provided_etag = calculate_sync_etag) - etag provided_etag - end + def calculate_customize_etag = nil + def calculate_sync_etag = nil + def customize = raise NotImplementedError + def finalize = raise NotImplementedError + def sync = raise NotImplementedError - data = sync - data[:children] = data[:children]&.map { |c| c.respond_to?(:render) ? c.render : c } - halt(data.to_json) + private + + STRATEGY = { + "/customize" => { data: :customize, etag: :calculate_customize_etag }, + # finalize calls should be rare and unique enough that we don't need to worry about ETags + "/finalize" => { data: :finalize }, + "/sync" => { data: :sync, etag: :calculate_sync_etag } + }.freeze + + def _call + return access_control_allow_methods if request.options? + return not_found unless request.post? + + @strategy = STRATEGY.fetch(request.path_info) { return not_found } + + headers = {} + + return Rack::Response[412, headers, []].to_a if etag_matches?(headers) + + Rack::Response[200, headers, processed_data].to_a end - options "/finalize" do - headers "Access-Control-Allow-Methods" => ["POST"] - halt 200 + def access_control_allow_methods + Rack::Response[200, { "access-control-allow-methods" => %w[POST] }, []].to_a end - post "/finalize" do - # finalize calls should be rare and unique enough that we don't need to worry about ETags - - data = finalize - data[:children] = data[:children]&.map { |c| c.respond_to?(:render) ? c.render : c } - halt(data.to_json) + def not_found + Rack::Response[404, { "x-cascade" => "pass" }, []].to_a end - options "/customize" do - headers "Access-Control-Allow-Methods" => ["POST"] - halt 200 + def etag_matches?(headers) + return false unless (calculator = @strategy[:etag]) + return false unless (raw_etag = public_send(calculator)) + + etag = +'"' << raw_etag << '"' + headers["etag"] = etag + + (none_match = request.get_header("HTTP_IF_NONE_MATCH")) && none_match.include?(etag) end - post "/customize" do - if (provided_etag = calculate_customize_etag) - etag provided_etag + def processed_data + data = public_send(@strategy[:data]) + + if data[:children] + data[:children] = data[:children].map { |c| c.respond_to?(:render) ? c.render : c } end - halt(customize.to_json) + data.to_json end - - def calculate_customize_etag = nil - def calculate_sync_etag = nil - def customize = raise NotImplementedError - def finalize = raise NotImplementedError - def sync = raise NotImplementedError end end