2012-02-02 10 views
12

Ho prima saputo di Data, context, and interaction (DCI) tramite this blog post. Affascinato dal concetto, ho cercato di integrarlo nella mia prossima applicazione Rails. Dato che DCI lavora in tandem con MVC, ho pensato che non sarebbe stato troppo difficile rendere l'API RESTful allo stesso tempo. Quindi ho creato una risorsa RESTful, Report e l'ho estesa con vari contesti. Il modo in cui ho implementato i contesti in Rails è stato creando una directory, /app/contexts/, per i moduli che estendono le azioni del controller. Quindi il mio reports_controller.rb assomiglia a questo:Contesti DCI RESTful 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 

E nel application_controller.rb ho impostato il contesto con un 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 

Avviso porgo current_user con un Role in aggiunta al contesto controller.

Ecco come funziona:

  1. un utente si collega
  2. ruolo dell'utente è RegisteredUser..
  3. RegisteredUser Il contesto predefinito è Search (come definito in /app/roles/registered_user.rb).
  4. All'interno del contesto Search, l'utente può solo visualizzare i report pubblicati.
  5. L'utente preme il pulsante "crea nuovo report" e il contesto viene modificato in Submission e memorizzato nella sessione di current_user.
  6. L'utente procede quindi a inviare un rapporto attraverso un modulo a più fasi.
  7. Ogni volta che l'utente salva il report passando il modulo, il contesto /app/contexts/submission.rb gestisce l'azione.

Sono diversi altri contesti (revisione, editoriale, ecc.) E ruoli (co-autore, editor, ecc.).

Finora questo approccio ha funzionato bene per la maggior parte. Ma c'è un difetto: quando un utente apre più finestre del browser e cambia i contesti in uno di essi, tutte le altre finestre si troveranno nel contesto sbagliato. Questo potrebbe essere un problema se l'utente si trova nel mezzo del modulo multi-step e quindi apre una finestra nel contesto Search. Quando si torna al modulo e si preme "Avanti", il controller eseguirà l'azione definita dal contesto Search anziché dal contesto Submission.

Ci sono 2 modi possibili in tutto questo che mi viene in mente:

  1. Namespace la risorsa Report con il nome del contesto. Quindi l'utente visiterebbe gli URL come /search/reports e /submission/reports/1. Questo non mi sembra RESTful e preferirei mantenere l'URL il più pulito possibile.
  2. Inserire il nome del contesto in un campo nascosto. Questo metodo richiede agli sviluppatori di ricordare di inserire il campo nascosto in ogni forma sul sito e non funziona per le richieste GET.

Esistono altri modi per risolvere questo problema o implementazioni generali migliori?

So di this project, ma è troppo limitato per le nostre esigenze.

+1

Haveyou ha esaminato gli esempi di Rickard Öberg di fare DCI e REST in Java con Qi4J. Credo che utilizzi la struttura URI per costruire il contesto, tale che la richiesta stessa porti tutte le informazioni necessarie per ricostruire il contesto sul server. Potresti anche provare a postare questo nel gruppo google di composizione degli oggetti. –

+0

forse memorizzare il passaggio precedente nei cookie può aiutare? – Fivell

+0

Il contesto corrente è già memorizzato nella sessione tramite cookie. Non vedo cosa memorizzare il passaggio precedente in questa situazione. Sarebbe anche reso inutile eseguendo un'azione in un'altra finestra del browser. –

risposta

3

Se si desidera consentire più contesti, ovviamente è necessario inserire le informazioni che determinano il contesto corrente in una memoria che non è condivisa tra le schede. Le sessioni, come implementate in Rack/Rails, utilizzano i cookie e i cookie sono condivisi tra le schede.

Basta inserire il contesto in qualcosa, che non è condiviso. Che ne dici di un parametro context = viewer URL?

Per parlare di REST penso che sia discutibile se una risorsa è uguale o diversa in contesti diversi. Si potrebbe sostenere che un rapporto per un utente "Visitatore" è diverso da un rapporto per un utente "Amministratore". In tal caso, un approccio RESTy potrebbe probabilmente assegnare un nome alle richieste (che di nuovo inserisce il contesto nell'URL), ad es./visitando/rapporti/1 vs/amministrazione/rapporti/1.

E un terzo modo per inserire il contesto nell'URL sarebbe usarlo come parte del nome del dominio.

+0

Ho finito per usare il namespace. –