jsonapi-suite / jsonapi_compliable

MIT License
20 stars 35 forks source link

Major sideloading overhaul #106

Closed richmolj closed 6 years ago

richmolj commented 6 years ago

To kick off 1.0 development, this PR overhauls sideloading. It is focused on reads-only, and does not account for polymorphic relationships. Additional development will continue in the 1.0.0-dev branch.

We previously allowed a nested syntax like this:

allow_sideload :foo do
  allow_sideload :bar do
    allow_sideload :baz
  end
end

This is a holdover from before Resources were introduced. It caused code complexity because the allow_sideload logic needed to live on both Resource and Sideload. The logic Resources need for sideloading is now defined in a mixin - resource/sideloading.rb and this type of nesting is no longer supported.

This opens the door for more a more well-defined Sideload interface. allow_sideload now (optionally) accepts a Sideload class, instead of defining everything in-line. This also allows has_many-type macros out-of-the-box, even for POROs. The only thing needed is a scope proc.

The ActiveRecord adapter now implements this new interface, simplifying the adapter and removing the need for sideloading_module. It also uses the interface to derive the various options, meaning we can now use one-liners to define associations.

This is all best illustrated in code:

 # PORO
has_many :comments do
  scope do |posts|
    # ... code ...
  end
end

 # via Sideload class
has_many :comments, class: CommentSideload

 # See sideload/*.rb and ActiveRecord adapter for examples
class CommentSideload < JsonapiCompliable::Sideload::HasMany
  def scope(parents)
    base_scope.where(post_id: parents.map(&:id))
  end

  def assign_each(post, comments)
    # custom logic
  end
end

 # you can still pass a block even when using a class
 # ( block code "wins" )
has_many :comments, class: CommentSideload do
  assign do |posts, comments|
    # ... code ...
  end
end

 # Options are still accepted
has_many :comments,
  resource: CommentResource,
  base_scope: -> { Comment.all },
  foreign_key: :post_id

 # assign still exists, but the more-common assign_each
 # now also exists with a simpler interface
 # (this will probably be renamed in a future commit)
has_many :comments do
  assign_each do |post, comments|
    comments.select { |c| c.post_id == post.id }
  end
end

 # if using ActiveRecord
has_many :comments
belongs_to :post
has_one :bio
many_to_many :teams

I have purposefully not made all tests pass as I am saving for future refactors. The tests passing here are:

In addition, I removed YARD docs every time I touched a method. The thought being a separate docs-only commit will come in the future, but for now comments just add a lot of noise.

Other notables:

richmolj commented 6 years ago

@wadetandy sorry for the bomb of a commit but this really shook up the fundamentals and I wanted to make sure the interface would still support all these associations (polymorphic-aside).