davydovanton / hanami-pagination

MIT License
14 stars 7 forks source link

Question: how to test actions with DI and all_for_page method #10

Open davydovanton opened 6 years ago

davydovanton commented 6 years ago

We have a problem now.

The first case

For example, we have the action like this one:

module Web::Controllers::Tasks
  class Index
    include Web::Action
    include Hanami::Pagination::Action
    expose :tasks

    def initialize(repository: TaskRepository.new)
      @repo = repo
    end

    def call(params)
      repo = @repo.all
      @tasks = all_for_page(repo)
    end
  end
end

And we want to test a action with using DI:

let(:action) { described_class.new(repository: mock_repo) }
let(:mock_repository) { double(:mock_repo, all: list_of_tasks) }

it { expect(action.call).to eq ... }

Now we have a problem, because #all_for_page method does some relation logic in action. And in this case we have a trouble with using double object with DI.

The second case

For example, we have the action like this one:

module Web::Controllers::Tasks
  class Index
    include Web::Action
    include Hanami::Pagination::Action
    expose :tasks

    def initialize(interactor: TaskListInteractor.new)
      @interactor = interactor
    end

    def call(params)
      result = @interactor.call(params)

      if result.success?
        @tasks = ... # WHAT?
      else
        @errors = result.errors
      end
    end
  end
end

In this case we need to return relation from interactor object and it's look like a problem. Because action works with relation instead of data.

davydovanton commented 6 years ago

After small talk with Luca we decided to use #map_to method from ROM relation.

Something like:

post.where(published: true).map_to(Paginated(Post)).to_a

DangerDawson commented 5 years ago

@davydovanton how did you use the #map_to method to solve the problem?

Are you still using the all_for_page helper with this or have you replaced it?

davydovanton commented 5 years ago

hey, we can easily create a method which will add pagination attributes to entity Paginated(Post). Like we have with Types::Collection(Project) type in entity's attributes block.

Now I don't use pagination in my projects that's why all_for_page still valid

DangerDawson commented 5 years ago

@davydovanton thank you for your reply.

I ended up rewriting all_for_page so it expects entities and a pager back from the repository

 def all_for_page(relation, conditions: {})
   relation.all_with_pager_by(conditions.merge(per_page: limit, page: (params[:page] || 1)))
 end

And in the repository

  def all_with_pager_by(conditions)
    per_page = conditions.delete(:per_page) || 10
    page = conditions.delete(:page) || 1
    relation = composite.where(conditions).per_page(per_page).page(page)
    [relation.to_a, Hanami::Pagination::Pager.new(relation.pager)]
  end

This allowed me to use DI with the controller with the repository, and also had the nicety of not returning a relation to the controller