cerebris / jsonapi-resources

A resource-focused Rails library for developing JSON:API compliant servers.
http://jsonapi-resources.com
MIT License
2.32k stars 529 forks source link

Broken support of ActiveModel resources in 0.10.x #1306

Open romikoops opened 4 years ago

romikoops commented 4 years ago

Based on gem documentation it is possible to create some service for ActiveModel resource. In fact, it does not work anymore in 0.10.x

The gem documentation has to be updated on wiki https://github.com/cerebris/jsonapi-resources/wiki/Using-JSONAPI::Resources-with-non-ActiveRecord-models-and-service-objects

There is some code examples in tests, but they are very complicated for such simple task.

https://github.com/cerebris/jsonapi-resources/blob/master/test/fixtures/active_record.rb#L1545

Please provide a real working example. It is a pain in ass to hack the gem to provide simple API for non ActiveRecord resource. I have very simple use case: returning list of all timezones via API:

ActiveSupport::TimeZone.all.map { |z| [z.name, z.to_s] }
yourivdlans commented 3 years ago

I'm also trying to get a simple non-activerecord backed model to work with this gem. After looking at the tests you referenced I was able to extract a basic implementation for getting this working.

Using this example you'd only have to add attributes to the resource and stat model to get new attributes working. By using attribute methods (hits in this case) on the StatResource you would also be able to use context like you would regularly.

StatResource

class StatResource < PoroResource
  singleton singleton_key: -> (context) {
    1
  }

  attribute :hits

  class << self
    def find_records(filters, options = {})
      [Stat.new(1)]
    end

    def find_records_by_keys(keys, options = {})
      [Stat.new(1)]
    end
  end

  def hits
    10
  end
end

Stat model

class Stat
  include ActiveModel::Model

  def initialize(id = nil)
    @id = id
  end

  attr_accessor :id, :hits
end

PoroResource

class PoroResource < JSONAPI::BasicResource
  root_resource

  class << self
    # Records
    def find_fragments(filters, options = {})
      fragments = {}
      find_records(filters, options).each do |record|
        rid = JSONAPI::ResourceIdentity.new(resource_klass, record.id)
        fragments[rid] = JSONAPI::ResourceFragment.new(rid)
      end
      fragments
    end

    def resource_klass
      self
    end

    def find_to_populate_by_keys(keys, options = {})
      find_by_keys(keys, options)
    end

    # Returns an array of Resources identified by the `keys` array
    #
    # @param keys [Array<key>] Array of primary keys to find resources for
    # @option options [Hash] :context The context of the request, set in the controller
    def find_by_keys(keys, options = {})
      records = find_records_by_keys(keys, options)
      resources_for(records, options[:context])
    end
  end
end

Routes:

jsonapi_resource :stats, only: :show

Stats controller

class Api::StatsController < Api::BaseController
  include JSONAPI::ActsAsResourceController
end