RESTful контексты DCI в Rails - PullRequest
       32

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

12 голосов
/ 02 февраля 2012

Впервые я узнал о данных, контексте и взаимодействии (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 в дополнение к контексту контроллера.

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

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

Существует несколько других контекстов (рецензия, редакционная статья и т. Д.) И роли (соавтор, редактор и т.

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

Есть два возможных пути решения этой проблемы:

  1. Пространство имен ресурса Report с именем контекста. Таким образом, пользователь будет посещать такие URL, как /search/reports и /submission/reports/1. Это не кажется мне УДОБНЫМ, и я бы предпочел, чтобы URL были максимально чистыми.
  2. Поместите имя контекста в скрытое поле. Этот метод требует, чтобы разработчики помнили, что нужно скрыть поле в каждой форме на сайте, и он не работает для запросов GET.

Есть ли другие способы обойти эту проблему или более эффективные реализации?

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

1 Ответ

3 голосов
/ 18 февраля 2012

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

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

Чтобы поговорить о REST, я считаю спорным, является ли ресурс одним и тем же или нет в разных контекстах.Можно утверждать, что отчет для «посещающего» пользователя отличается от отчета для «управляющего» пользователя.В этом случае подход RESTy, вероятно, будет использовать пространство имен для запросов (что снова помещает контекст в URL), например, / visit / reports / 1 vs /administering/reports/1.

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

...