Впервые я узнал о данных, контексте и взаимодействии (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
. Это не кажется мне УДОБНЫМ, и я бы предпочел, чтобы URL были максимально чистыми.
- Поместите имя контекста в скрытое поле. Этот метод требует, чтобы разработчики помнили, что нужно скрыть поле в каждой форме на сайте, и он не работает для запросов GET.
Есть ли другие способы обойти эту проблему или более эффективные реализации?
Я знаю о этом проекте , но он слишком ограничен для наших нужд.