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

Как реализовать ресурс RESTful для конечного автомата или конечных автоматов

Я новичок Rails и REST, и я пытаюсь понять, как лучше всего открыть ресурс, который поддерживается объектом домена, который имеет конечный автомат (другими словами, это конечные автоматы).

Я видел несколько камней для создания модельного класса конечной машины, такой как aasm, переходы, рабочий процесс, но ни один из них не документирует примеры того, как они фактически используются в ориентированном на ресурсы контроллере. Все они, похоже, подразумевают, что переходы состояния инициируются "событием", которое действительно является вызовом метода. Некоторые вопросы, которые я имею в связи с этим:

  • Действие обновления (метод PUT) не подходит, потому что PUT предполагается идемпотентным. Только это было бы возможно, если бы государство было отправлено как часть представления. Это несогласие с "событием". Правильно ли это?
  • Так как события не являются идемпотентными, то необходимо использовать POST. Но, на какой ресурс? Есть ли субресурс для каждого возможного события? Или есть один (/updatestate), который принимает в качестве своего представления событие для запуска и любые параметры для события?
  • Поскольку состояние ресурса модифицируется событием, инициируемым потенциально другим ресурсом, должно ли действие create accept изменять атрибут состояния (или любые другие атрибуты, зависящие от конечного автомата)?
  • [Обновленный вопрос] Что такое хороший способ разоблачения переходов в пользовательском интерфейсе? Поскольку события не являются состояниями, кажется, что нет смысла разрешать обновлять атрибут состояния (и любой другой атрибут, зависящий от переходов состояний). Означает ли это, что эти атрибуты следует игнорировать в действии обновления?
4b9b3361

Ответ 1

  • Действие обновления (метод PUT) не подходит, потому что PUT предполагается идемпотентным. Только это было бы возможно, если бы государство было отправлено как часть представления. Это несогласие с "событием". Правильно ли это?

Правильно.

  • Так как события не являются идемпотентными, то необходимо использовать POST. Но, на какой ресурс? Есть ли субресурс для каждого возможного события? Или есть один (/updatestate), который принимает в качестве своего представления событие для запуска и любые параметры для события?

Вы можете сделать это в обоих направлениях. Вы можете поддерживать как в одном приложении, так и при изменении типов событий, определяемых либо входящим документом, либо получающим ресурсом. Лично я бы предпочел сделать это разными типами документов, но это только мое мнение. Если вы переходите с маршрута нескольких ресурсов, убедитесь, что они доступны для поиска (то есть, имея ссылки на каждый из них, описанный в документе, возвращаемом при получении родительского ресурса).

  • Поскольку состояние ресурса модифицируется событием, инициируемым потенциально другим ресурсом, должно ли действие create accept изменять атрибут состояния (или любые другие атрибуты, зависящие от конечного автомата)?

До вас; нет никакой реальной причины, почему вы должны уделять пристальное внимание какому-либо конкретному атрибуту создания. (Вы могли бы рационализировать это, сказав, что состояние переходит в правильное начальное состояние для конечного автомата сразу после создания.) В государственных машинах, которые я сделал, создание было POST в любом случае (и другого - довольно сложного) документ), так что все это было спорно, но если вы позволите несколько начальных состояний, то это имеет смысл взять "это мой предпочтительный начальное состояние" намек в документе создания. Чтобы быть понятным, просто потому, что пользователь хочет этого, это не значит, что вам нужно это делать; хотите ли вы жаловаться на пользователя, когда вы отклоняете его предложение, является вашим вызовом.

  • Элемент списка

[Ответ на запас.]

Ответ 2

Немного опоздал на вечеринку здесь, но я изучил эту точную проблему для себя и обнаружил, что драгоценный камень, который я сейчас использую для управления моими государственными машинами (state_machine от pluginaweek) имеет некоторые методы, которые справляются с этой проблемой довольно хорошо.

При использовании с ActiveRecord (и я также предполагаю другие уровни сохранения), он предоставляет метод #state_event=, который принимает строковое представление события, которое вы хотели бы запустить. См. Документацию здесь.

# For example,

vehicle = Vehicle.create          # => #<Vehicle id: 1, name: nil, state: "parked">
vehicle.state_event               # => nil
vehicle.state_event = 'invalid'
vehicle.valid?                    # => false
vehicle.errors.full_messages      # => ["State event is invalid"]

vehicle.state_event = 'ignite'
vehicle.valid?                    # => true
vehicle.save                      # => true
vehicle.state                     # => "idling"
vehicle.state_event               # => nil

# Note that this can also be done on a mass-assignment basis:

vehicle = Vehicle.create(:state_event => 'ignite')  # => #<Vehicle id: 1, name: nil, state: "idling">
vehicle.state                                       # => "idling"

Это позволяет просто добавить поле state_event в формы редактирования ресурсов и получить переходы состояний так же легко, как и обновление любого другого атрибута.

Теперь мы, очевидно, все еще используем PUT для запуска событий с использованием этого метода, который не является RESTful. Однако жемчужина предоставляет интересный пример, который по крайней мере "чувствует" вполне RESTful, несмотря на то, что он использует тот же метод non-RESTful под обложками.

Как вы можете видеть здесь и здесь, возможности самоанализа самоцвета позволяют вам представлять в ваших формах либо событие, в котором вы хотели бы включить или имя этого события.

<div class="field">
  <%= f.label :state %><br />
  <%= f.collection_select :state_event, @user.state_transitions, :event, :human_to_name, :include_blank => @user.human_state_name %>
</div>

<div class="field">
  <%= f.label :access_state %><br />
  <%= f.collection_select :access_state_event, @user.access_state_transitions, :event, :human_event, :include_blank => "don't change" %>
</div>

Используя последнюю технику, вы получаете простое обновление состояния модели до любого допустимого следующего состояния без необходимости писать дополнительный код. Это не технически RESTful, но это позволяет вам легко представить его таким образом в пользовательском интерфейсе.

Чистота этой методики в сочетании с присущими конфликтами попытками создать государственную машину на основе событий в простой ресурс RESTful была достаточной, чтобы удовлетворить меня, поэтому, надеюсь, она также дает вам некоторое представление.

Ответ 3

Если ваш ресурс имеет какой-то атрибут статуса, вы можете использовать метод micro-PUT для обновления его статуса.

PUT /Customer/1/Status
Content-Type: text/plain

Closed

=> 200 OK
Content-Location: /Customer/1

Вы можете моделировать состояния ресурсов в виде коллекций и перемещать ресурсы между этими коллекциями.

GET /Customer/1
=>
Content-Type: application/vnd.acme.customer+xml
200 OK


POST /ClosedCustomers
Content-Type: application/vnd.acme.customer+xml
=>
200 OK

POST /OpenCustomers
Content-Type: application/vnd.acme.customer+xml
=>
200 OK

Вы всегда можете использовать новый метод PATCH

PATCH /Customer/1
Content-Type: application/x-www-form-urlencoded
Status=Closed
=>
200 OK

Ответ 4

Бит поздно на вечеринку здесь и далеко от эксперта, поскольку у меня есть похожий запрос, но...

Как сделать событие ресурсом?

Итак, вместо...

PUT /order/53?state_event="pay" #Order.update_attributes({state_event: "pay})

Вы бы...

POST /order/53/pay     #OrderEvent.create(event_name: :pay)
POST /order/53/cancel  #OrderEvent.create(event_name: :cancel)

С помощью pub/sub listener между Order и OrderEvent или обратным вызовом, который пытается запустить это событие на Order и записывать сообщения перехода. Он также дает вам удобный аудит всех событий изменения состояния.

Идея украдена из Виллем Берген в Shopify

Я что-то упустил? Извините, пытаясь понять это сам.