Skip to content
This repository was archived by the owner on Oct 19, 2018. It is now read-only.

3 Book Operation examples

Barrie Hadfield edited this page Jan 5, 2017 · 3 revisions

@barriehadfield

This example raises some interesting questions

  1. currently acting_user is defined for the sake of regulation only, and is only available on the server side, inside of regulation. But you are the second person to intuitively believe it should be there... So there is some work to be done to make that happen 👍

  2. When sending models back to the server should we send the unsaved state or the last saved state? Both are possible, but certainly just referencing the model in its last saved state is easier. However imagine you were running on the server and you added a book to a cart, and then without saving the cart invoked an operation on the cart. You would certainly expect the operation to accept the in-memory cart, and then do stuff to it. So I think in fact we need to send the current unsaved state to the server. If the server saves it, the changes will get reflected by synchromesh.

  3. This leads to the next point. Lets say an operation is currently authorized for the acting_user. Okay fine. But lets say inside that operation we attempt to access some data that is NOT authorized for that user. Do we accept that as okay or abort and rollback.

# Use case: we have a list of books and want to add one to the users basket

module Components
  module Books
    class List < React::Component::Base

      render(DIV) do
        Book.all.each do |book|
          li { "Add #{book.name}" }.on(:click) do
            AddBookToBasket(book: book) do |outcome|
              alert "Failed to add the book" unless outcome.success?
            end
          end
        end
      end
    end
  end
end

module Operations
  module Books
    class AddBookToBasket < HyperLoop::Operation

      param :book, type: Book

      def execute
        # unless acting_user.basket.include? book
        # no need for the unless... if book is already in the basket the next to lines
        # will do nothing anyway
        acting_user.basket << book #(just like active record, only pushes if book is not already there)
        acting_user.save #(unlike active record, we don't save unless there are changes to save)
        # end
        unless book.interested_users.include? acting_user
          book.interested_users << acting_user
          book.save
          EmailUserAboutBook(book: params.book, user: acting_user)
          # would it be better to make this whole execute block server only?
          # guess that is my choice - new Operation or make this execute server only
        end
        # This feels better to me BTW (see my version of the operation below):
        UpdateInterestedUsers(book: params.book)
      end
    end

    class UpdateInterestedUsers < HyperLoop::Operation
      param :book, type: Book
      allow_remote_operation { acting_user }
      
      def execute
        return if params.book.interested_users.include? acting_user
        params.book.interested_users << acting_user
        params.book.save
        UserMailer.book_email(acting_user, params.book)
      end
    end

    class EmailUserAboutBook < HyperLoop::Operation

      param :book, type: Book
      param :user, type: User
      allow_operation { acting_user }

      execute(:server) do
        # client code knows nothing about UserMailer...
        UserMailer.book_email(params.user, params.book)
      end

    end

  end
end
Clone this wiki locally