module ViewModel::Controller
def _extract_param_hash(data)
def _extract_param_hash(data) case data when Hash data when ActionController::Parameters data.to_unsafe_h else raise ViewModel::Error.new(status: 400, detail: "Invalid data submitted, expected hash: #{data.inspect}") end end
def _extract_update_data(data)
def _extract_update_data(data) if data.is_a?(Array) if data.blank? raise ViewModel::Error.new(status: 400, detail: "No data submitted: #{data.inspect}") end data.map { |el| _extract_param_hash(el) } else _extract_param_hash(data) end end
def encode_jbuilder
def encode_jbuilder builder = Jbuilder.new do |json| yield json end ViewModel.encode_json(builder.attributes!) end
def parse_bulk_update
def parse_bulk_update data, references = parse_viewmodel_updates ViewModel::Schemas.verify_schema!(ViewModel::Schemas::BULK_UPDATE, data) updates_by_parent = data.fetch(ViewModel::BULK_UPDATES_ATTRIBUTE).each_with_object({}) do |parent_update, acc| parent_id = parent_update.fetch(ViewModel::ID_ATTRIBUTE) update = parent_update.fetch(ViewModel::BULK_UPDATE_ATTRIBUTE) acc[parent_id] = update end return updates_by_parent, references end
def parse_viewmodel_updates
def parse_viewmodel_updates data_param = params.fetch(:data) do raise ViewModel::Error.new(status: 400, detail: "Missing 'data' parameter") end refs_param = params.fetch(:references, {}) update_hash = _extract_update_data(data_param) refs = _extract_param_hash(refs_param) return update_hash, refs end
def prerender_json_view(json_view, json_references: {})
def prerender_json_view(json_view, json_references: {}) json_view = wrap_json_view(json_view) json_references = wrap_json_view(json_references) encode_jbuilder do |json| json.data json_view if json_references.present? json.references do json_references.sort.each do |key, value| json.set!(key, value) end end end yield(json) if block_given? end end
def prerender_viewmodel(viewmodel, status: nil, serialize_context: viewmodel.class.try(:new_serialize_context))
def prerender_viewmodel(viewmodel, status: nil, serialize_context: viewmodel.class.try(:new_serialize_context)) encode_jbuilder do |json| json.data do ViewModel.serialize(viewmodel, json, serialize_context: serialize_context) end if serialize_context && serialize_context.has_references? json.references do serialize_context.serialize_references(json) end end yield(json) if block_given? end end
def render_error(error_view, status = 500)
def render_error(error_view, status = 500) unless error_view.is_a?(ViewModel) raise "Expected ViewModel error view, received #{error_view.inspect}" end render_jbuilder(status: status) do |json| json.error do ctx = error_view.class.new_serialize_context(access_control: ViewModel::AccessControl::Open.new) ViewModel.serialize(error_view, json, serialize_context: ctx) end end end
def render_jbuilder(status:)
def render_jbuilder(status:) response = encode_jbuilder do |json| yield json end render_json_string(response, status: status) end
def render_json_string(response, status: nil)
def render_json_string(response, status: nil) render(json: response, status: status) end
def render_json_view(json_view, json_references: {}, status: nil, &block)
JSON string terminals. Useful for rendering cached views without parsing
Render an arbitrarily nested tree of hashes and arrays with pre-rendered
def render_json_view(json_view, json_references: {}, status: nil, &block) prerender = prerender_json_view(json_view, json_references: json_references, &block) render_json_string(prerender, status: status) end
def render_viewmodel(viewmodel, status: nil, serialize_context: viewmodel.class.try(:new_serialize_context), &block)
def render_viewmodel(viewmodel, status: nil, serialize_context: viewmodel.class.try(:new_serialize_context), &block) prerender = prerender_viewmodel(viewmodel, serialize_context: serialize_context, &block) render_json_string(prerender, status: status) end
def wrap_json_view(view)
def wrap_json_view(view) case view when Array view.map { |v| wrap_json_view(v) } when Hash view.transform_values { |v| wrap_json_view(v) } when String, Symbol CompiledJson.new(view) else view end end