Подтвердить что ты не робот

Контексты DCI RESTful в Rails

Я впервые узнал о Данные, контекст и взаимодействие (DCI) через этот пост в блоге. Увлекаясь этой концепцией, я попытался собрать ее в свое следующее приложение Rails. Поскольку DCI работает в тандеме с MVC, я думал, что не будет слишком сложно сделать API RESTful одновременно. Поэтому я создал ресурс RESTful, Report и расширил его с помощью различных контекстов. То, как я реализовал контексты в Rails, было создание каталога /app/contexts/ для модулей, которые расширяют действия контроллера. Итак, мой reports_controller.rb выглядит так:

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

И в application_controller.rb я установил контекст с помощью 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

Обратите внимание, что я расширяю current_user с помощью Role в дополнение к контексту контроллера.

Вот как это работает:

  • Пользователь регистрируется.
  • Роль пользователя RegisteredUser.
  • RegisteredUser контекст по умолчанию Search (как определено в /app/roles/registered_user.rb).
  • В контексте Search пользователь может просматривать только опубликованные отчеты.
  • Пользователь нажимает кнопку "Создать новый отчет", а контекст изменяется на Submission и сохраняется в сеансе current_user.
  • Затем пользователь отправляет отчет через многоэтапную форму.
  • Каждый раз, когда пользователь сохраняет отчет, перейдя через форму, контекст /app/contexts/submission.rb обрабатывает действие.

Есть несколько других контекстов (обзор, редакторский и т.д.) и роли (соавтор, редактор и т.д.).

До сих пор этот подход работал по большей части. Но есть недостаток: когда пользователь открывает несколько окон браузера и меняет контексты в одном из них, все остальные окна будут в неправильном контексте. Это может быть проблемой, если пользователь находится в середине многоэтапной формы, а затем открывает окно в контексте Search. Когда он переключается на форму и нажимает "Далее", контроллер будет выполнять действие, определяемое контекстом Search вместо контекста Submission.

Есть два возможных способа, которыми я могу думать:

  • Пространство имен: ресурс Report с именем контекста. Таким образом, пользователь будет посещать URL-адрес, например /search/reports и /submission/reports/1. Это не кажется RESTful для меня, и я предпочел бы сохранить URL как можно более чистым.
  • Поместите имя контекста в скрытое поле. Этот метод требует, чтобы разработчики не забыли поместить скрытое поле в каждую форму на сайте, и оно не работает для запросов GET.

Есть ли другие способы решения этой проблемы или улучшения общих реализаций?

Я знаю этот проект, но он слишком ограничен для наших нужд.

4b9b3361

Ответ 1

Если вы хотите разрешить несколько контекстов, то, очевидно, вы должны поместить информацию, которая определяет текущий контекст в некотором хранилище, которое не используется между вкладками. Сеансы, реализованные в Rack/Rails, используют файлы cookie, а файлы cookie совместно используются вкладками.

Просто поставьте контекст во что-то, что не является общим. Как насчет параметра context = viewer URL?

Для того, чтобы говорить REST я думаю, что это спорно, является ли ресурс такой же или нет в различных контекстах. Можно утверждать, что отчет для пользователя "Посещение" отличается от отчета для пользователя "Администрирование". В этом случае подход RESTy, вероятно, будет содержать пробелы в запросах (которые снова помещают контекст в URL-адрес), например./посещение/отчеты/1 против/администрирование/отчеты/1.

И третий способ поместить контекст в URL-адрес будет использовать его как часть имени домена.