ruby-grape / grape-active_model_serializers

User active_model_serializers with Grape
MIT License
140 stars 68 forks source link

API Versioning: Supporting Active Model Serializers namespacing #48

Closed syntaxTerr0r closed 8 years ago

syntaxTerr0r commented 8 years ago

Hi,

I have a versioned API with versioned Serializers. Here is my tree:

app/
  api/
    v1/
      base.rb # V1::Base
    v2/
      base.rb # V2::Base
  api.rb

  serializers/
    v1/
      my_shiny_serializer.rb # V1::MyShinySerializer
    v2/
      my_shiny_serializer.rb # V2::MyShinySerializer

I had to monkey patch this gem like in the following code to make it supports the ActiveModelSerializer feature that allow it to look for namespaced Serializers, please see comments in the patch below.

My question is: what is the best way to properly add this feature to the gem?

module Grape
  module Formatter
    module ActiveModelSerializers
      class << self
        def fetch_serializer(resource, env)
          endpoint = env['api.endpoint']
          options = build_options_from_endpoint(endpoint)
          # Monkey patch is essentially here:
          # First I retrieve the current module in which Grape's block is declared
          klass = endpoint.options[:for].to_s
          # Then, I extract the top module name (ie: V1, V2) in the fashion of Rails Inflectors "demodulize"
          # but instead of extracting top right Constant, I take the top left Constant
          namespace = klass[0, klass.index('::') || 0]

          # Finally, I pass the extracted namespace to ActiveModelSerializer 
          # so that it can find my serializer in the good namespace/directory
          serializer = options.fetch(:serializer, ActiveModel::Serializer.serializer_for(resource, { namespace: namespace }))
          return nil unless serializer

          options[:scope] = endpoint unless options.key?(:scope)
          # ensure we have an root to fallback on
          options[:resource_name] = default_root(endpoint) if resource.respond_to?(:to_ary)
          serializer.new(resource, options.merge(other_options(env)))
        end
      end
    end
  end
end
dblock commented 8 years ago

I am not sure what the "official" implementation should look like, but I would start by writing some failing specs against master here.

syntaxTerr0r commented 8 years ago

@dblock: I can do that for sure :)

syntaxTerr0r commented 8 years ago

I updated my PR with a solution I suggest. Inconvenient: this is a breaking change for Grape's users that already have declared one or more versions. They shall add namespace to their existing serializers.

dblock commented 8 years ago

Closed via https://github.com/jrhe/grape-active_model_serializers/pull/49.