Conocí por primera vez acerca de Data, context, and interaction (DCI) a través de this blog post. Fascinado por el concepto, me esforcé por incorporarlo a mi próxima aplicación Rails. Dado que DCI funciona en conjunto con MVC, pensé que no sería demasiado difícil hacer la API RESTful al mismo tiempo. Así que hice un recurso RESTful, Report
y lo extendí en varios contextos. La forma en que implementé contextos en Rails fue creando un directorio, /app/contexts/
, para módulos que extienden las acciones del controlador. Así que mi reports_controller.rb
se parece a esto:RESTful DCI contexts in Rails
class ReportsController < ApplicationController
before_filter :only => :new do |c|
c.switch_context("submission")
end
# GET /reports
def index
@context.report_list
end
# GET /reports/1
def show
@context.display_report
end
# GET /reports/new
def new
@context.new_report
end
# GET /reports/1/edit
def edit
@context.edit_report
end
# POST /reports
def create
@context.create_report
end
def update
@context.update_report
end
# DELETE /reports/1
def destroy
@context.destroy_report
end
protected
def switch_context(context_name)
session[:context] = context_name
context = session[:context].camelize.constantize
@context ||= self.extend context
end
end
Y en el application_controller.rb
puse el contexto de una before_filter
:
class ApplicationController < ActionController::Base
before_filter :contextualize
protect_from_forgery
protected
# Sets the context of both current_user and self
# by extending with /app/roles/role_name
# and /app/contexts/context_name respectively
def contextualize
# Extend self (ActionController::Base) with context
if session[:context]
context_class = session[:context].camelize.constantize
if current_user.allowed_contexts.include?(context_class)
context_class = current_user.context if context_class == Visiting
else
context_class = Visiting
end
else
context_class = current_user.context
end
@context ||= self.extend context_class
end
end
Aviso extiendo current_user
con un Role
además del marco regulador.
Así es como funciona:
- Un usuario inicia sesión en
- El papel del usuario es
RegisteredUser
.. RegisteredUser
El contexto predeterminado esSearch
(como se define en/app/roles/registered_user.rb
).- Dentro del contexto
Search
, el usuario solo puede ver informes publicados. - El usuario presiona el botón "crear nuevo informe" y el contexto se cambia a
Submission
y se almacena en la sesióncurrent_user
. - El usuario procede a enviar un informe a través de un formulario de varios pasos.
- Cada vez que el usuario guarda el informe al recorrer el formulario, el contexto
/app/contexts/submission.rb
maneja la acción.
Los otros contextos (revisión, editorial, etc.) y roles (coautor, editor, etc.) son varios.
Hasta ahora, este enfoque ha funcionado bien en su mayor parte. Pero hay un error: cuando un usuario abre varias ventanas del navegador y cambia contextos en una de ellas, todas las otras ventanas estarán en el contexto incorrecto. Esto podría ser un problema si el usuario está en el medio del formulario de pasos múltiples y luego abre una ventana en el contexto Search
. Cuando vuelva al formulario y presione "Siguiente", el controlador realizará la acción definida por el contexto Search
en lugar del contexto Submission
.
Hay 2 posibles formas de evitar esto que me ocurre:
- espacio de nombres del
Report
recurso con el nombre de contexto. Por lo tanto, el usuario visitaría direcciones URL como/search/reports
y/submission/reports/1
. Esto no me parece RESTANTE y preferiría mantener la URL lo más limpia posible. - Ponga el nombre del contexto en un campo oculto. Este método requiere que los desarrolladores recuerden poner el campo oculto en todas las formas del sitio, y no funciona para las solicitudes GET.
¿Hay alguna otra forma de solucionar este problema, o mejores implementaciones generales?
Sé de this project, pero es demasiado limitado para nuestras necesidades.
Haveyou miró los ejemplos de Rickard Öberg de hacer DCI y REST en Java con Qi4J. Creo que usa la estructura de URI para construir el contexto, por lo que la solicitud en sí misma contiene toda la información necesaria para reconstruir el contexto en el servidor. También es posible que desee intentar publicar esto en el grupo de google de composición de objetos. –
tal vez el almacenamiento del paso anterior en las cookies puede ayudar? – Fivell
El contexto actual ya está almacenado en la sesión a través de una cookie. No veo qué almacenar en el paso anterior se sumaría a esta situación. También sería inútil al realizar alguna acción en otra ventana del navegador. –