Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API Versioning and Roar #106

Open
chadwtaylor opened this issue Feb 2, 2015 · 60 comments
Open

API Versioning and Roar #106

chadwtaylor opened this issue Feb 2, 2015 · 60 comments

Comments

@chadwtaylor
Copy link
Contributor

Everything worked great for a model. When it came to an array/collection of models, this is where I'm stuck and I wonder if it has to do with the fact my Rails API project is versioned (ie: v1, v2)?

The error message I'm getting when trying to load a collection:

uninitialized constant Api::V2::PeopleRepresenter (and I already instructed the render to use represent_items_with: Api::V2::PersonRepresenter). I wonder if it has to do with modules/namespacing things?

Here's what my set up looks like...

app/api/v2/api_controller

require 'roar'

module V2
  class Api::V2::ApiController < ActionController::API

    include Roar::Rails::ControllerAdditions
    include Roar::Rails::ControllerAdditions::Render

    respond_to :json
    ...
  end
end

app/api/v2/people_controller

class Api::V2::PeopleController < Api::V2::ApiController

  # GET /people
  def index
    people = Person.find([8,18])
    render json: people, represent_items_with: Api::V2::PersonRepresenter
  end

  ...

end

app/representers/api/v2/person_representer

module Api
  module V2
    class PersonRepresenter < Roar::Decorator
      include Roar::JSON::JSONAPI

      type :people

      property :id
      property :first_name
      property :last_name
      property :full_name

    end
  end
end
@apotonick
Copy link
Member

Have you looked into the ::represents class method, yet? https://github.com/apotonick/roar-rails#represents-configuration

@chadwtaylor
Copy link
Contributor Author

Since each method will vary in which model will be rendered (ie: most of the time a person model, rarely will be an invoice model). So putting it on the class level may not be favorable in my situation? I gave it a try though and got another error: undefined method 'id' for #<Array:0x007fde1099e670>

I'm hoping I can create one PersonRepresenter that will deal with collections as well, hence the represent_items_with at the method level.

Thanks for your invaluable help!

@apotonick
Copy link
Member

I'm still wondering, though, why the :represent_items_with option doesn't kick in?

@chadwtaylor
Copy link
Contributor Author

What else do you need to see from my end to narrow down the issue?

@chadwtaylor
Copy link
Contributor Author

I even tried to change from render json: people, represent_items_with:Api::V2::PersonRepresenter to respond_with people, represent_items_with:Api::V2::PersonRepresenter and got the following error:

ActionController::UnknownFormat

Am hitting a wall here but going to try few more things and hopefully get the collections to work (emphasis: The single model worked nicely).

@apotonick
Copy link
Member

Ah, I got it! The :represent_items_with option doesn't work with render! If you use respond_with you gotta call respond_to on the controller level (that's Rails, not roar-rails!)!

@chadwtaylor
Copy link
Contributor Author

I switched to respond_with with the respond_to on the controller level (see my codes below) -- and got this error:

ActionController::UnknownFormat

The only thing working for me is using render (on a single model). Any ideas?

# app/api/v2/people_controller.rb

class Api::V2::PeopleController < Api::V2::ApiController
  include Roar::Rails::ControllerAdditions
  respond_to :json

  def index
    p = Person.first
    respond_with p #=> This resulted in ActionController::UnknownFormat
  end

end


# app/representers/api/v2/person_representer.rb

module Api
  module V2
    module PersonRepresenter 
      include Roar::JSON::JSONAPI

      type :people

      property :id
      property :first_name
      property :last_name
      property :full_name

    end
  end
end

@apotonick
Copy link
Member

Can you paste the server output or the test line for that failling request? It must be something in Rails, we don't throw UnknownFormat anywhere! Thanks!

@chadwtaylor
Copy link
Contributor Author

Started GET "/v2/people" for 127.0.0.1 at 2015-02-03 14:15:35 -0800
Processing by Api::V2::PeopleController#index as HTML
  Person Load (0.5ms)  SELECT  "people".* FROM "people"  ORDER BY "people"."id" ASC LIMIT 1
Completed 406 Not Acceptable in 3ms

ActionController::UnknownFormat - ActionController::UnknownFormat:
  responders (2.1.0) lib/action_controller/respond_with.rb:205:in `respond_with'
  app/controllers/api/v2/people_controller.rb:12:in `index'
  actionpack (4.2.0) lib/abstract_controller/base.rb:198:in `process_action'
  actionpack (4.2.0) lib/action_controller/metal/rendering.rb:10:in `process_action'
  actionpack (4.2.0) lib/abstract_controller/callbacks.rb:20:in `block in process_action'
  activesupport (4.2.0) lib/active_support/callbacks.rb:117:in `call'
  activesupport (4.2.0) lib/active_support/callbacks.rb:169:in `block in halting'
  activesupport (4.2.0) lib/active_support/callbacks.rb:169:in `block in halting'
  activesupport (4.2.0) lib/active_support/callbacks.rb:92:in `_run_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:734:in `_run_process_action_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:81:in `run_callbacks'
  actionpack (4.2.0) lib/abstract_controller/callbacks.rb:19:in `process_action'
  actionpack (4.2.0) lib/action_controller/metal/rescue.rb:29:in `process_action'
  actionpack (4.2.0) lib/action_controller/metal/instrumentation.rb:31:in `block in process_action'
  activesupport (4.2.0) lib/active_support/notifications.rb:164:in `block in instrument'
  activesupport (4.2.0) lib/active_support/notifications/instrumenter.rb:20:in `instrument'
  activesupport (4.2.0) lib/active_support/notifications.rb:164:in `instrument'
  actionpack (4.2.0) lib/action_controller/metal/instrumentation.rb:30:in `process_action'
  activerecord (4.2.0) lib/active_record/railties/controller_runtime.rb:18:in `process_action'
  actionpack (4.2.0) lib/abstract_controller/base.rb:137:in `process'
  actionview (4.2.0) lib/action_view/rendering.rb:30:in `process'
  actionpack (4.2.0) lib/action_controller/metal.rb:195:in `dispatch'
  actionpack (4.2.0) lib/action_controller/metal/rack_delegation.rb:13:in `dispatch'
  actionpack (4.2.0) lib/action_controller/metal.rb:236:in `block in action'
  actionpack (4.2.0) lib/action_dispatch/routing/route_set.rb:73:in `dispatch'
  actionpack (4.2.0) lib/action_dispatch/routing/route_set.rb:42:in `serve'
  actionpack (4.2.0) lib/action_dispatch/journey/router.rb:43:in `block in serve'
  actionpack (4.2.0) lib/action_dispatch/journey/router.rb:30:in `serve'
  actionpack (4.2.0) lib/action_dispatch/routing/route_set.rb:802:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/rack/agent_hooks.rb:26:in `traced_call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:55:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/rack/browser_monitoring.rb:23:in `traced_call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:55:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/rack/developer_mode.rb:56:in `traced_call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:55:in `call'
  rack (1.6.0) lib/rack/etag.rb:24:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rack (1.6.0) lib/rack/conditionalget.rb:25:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rack (1.6.0) lib/rack/head.rb:13:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/params_parser.rb:27:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  activerecord (4.2.0) lib/active_record/query_cache.rb:36:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  activerecord (4.2.0) lib/active_record/connection_adapters/abstract/connection_pool.rb:647:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  activerecord (4.2.0) lib/active_record/migration.rb:378:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
  activesupport (4.2.0) lib/active_support/callbacks.rb:88:in `_run_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:734:in `_run_call_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:81:in `run_callbacks'
  actionpack (4.2.0) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/reloader.rb:73:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/remote_ip.rb:78:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  appsignal (0.11.0) lib/appsignal/rack/listener.rb:13:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  airbrake (4.1.0) lib/airbrake/rails/middleware.rb:13:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  better_errors (2.0.0) lib/better_errors/middleware.rb:84:in `protected_app_call'
  better_errors (2.0.0) lib/better_errors/middleware.rb:79:in `better_errors_call'
  better_errors (2.0.0) lib/better_errors/middleware.rb:57:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  railties (4.2.0) lib/rails/rack/logger.rb:38:in `call_app'
  railties (4.2.0) lib/rails/rack/logger.rb:20:in `block in call'
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:68:in `block in tagged'
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:26:in `tagged'
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:68:in `tagged'
  railties (4.2.0) lib/rails/rack/logger.rb:20:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/request_id.rb:21:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rack (1.6.0) lib/rack/runtime.rb:18:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rocket_pants (1.10.0) lib/rocket_pants/cache_middleware.rb:21:in `_call'
  rocket_pants (1.10.0) lib/rocket_pants/cache_middleware.rb:12:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  activesupport (4.2.0) lib/active_support/cache/strategy/local_cache_middleware.rb:28:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rack (1.6.0) lib/rack/lock.rb:17:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/static.rb:113:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rack-cors (0.3.1) lib/rack/cors.rb:72:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  airbrake (4.1.0) lib/airbrake/user_informer.rb:16:in `_call'
  airbrake (4.1.0) lib/airbrake/user_informer.rb:12:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  hirefire-resource (0.3.4) lib/hirefire/middleware.rb:29:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  railties (4.2.0) lib/rails/engine.rb:518:in `call'
  railties (4.2.0) lib/rails/application.rb:164:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rack (1.6.0) lib/rack/content_length.rb:15:in `call'
  puma (2.9.2) lib/puma/server.rb:490:in `handle_request'
  puma (2.9.2) lib/puma/server.rb:361:in `process_client'
  puma (2.9.2) lib/puma/server.rb:254:in `block in run'
  puma (2.9.2) lib/puma/thread_pool.rb:92:in `block in spawn_thread'

@chadwtaylor
Copy link
Contributor Author

(want to say how much I appreciate your help here!)

@apotonick
Copy link
Member

"No worries, mate!" as they say in Australia! 😉

This is your problem: Processing by Api::V2::PeopleController#index as HTML

Your request is not a JSON request! That's why Rails complains, it doesn't know how to handle a generic HTML request.

@chadwtaylor
Copy link
Contributor Author

Ah, so I appended .json to the url like so: http://localhost:3001/v2/people.json and a new error came up:

Started GET "/v2/people.json" for 127.0.0.1 at 2015-02-03 14:25:17 -0800
Processing by Api::V2::PeopleController#index as JSON
  Person Load (0.3ms)  SELECT  "people".* FROM "people"  ORDER BY "people"."id" ASC LIMIT 1
Completed 500 Internal Server Error in 18ms

NoMethodError - undefined method `default_render' for #<Api::V2::PeopleController:0x007fdf3bb770d8>:
  responders (2.1.0) lib/action_controller/responder.rb:236:in `default_render'
  responders (2.1.0) lib/action_controller/responder.rb:186:in `to_format'
  responders (2.1.0) lib/action_controller/responder.rb:163:in `respond'
  responders (2.1.0) lib/action_controller/responder.rb:156:in `call'
  responders (2.1.0) lib/action_controller/respond_with.rb:203:in `respond_with'
  app/controllers/api/v2/people_controller.rb:12:in `index'
  actionpack (4.2.0) lib/abstract_controller/base.rb:198:in `process_action'
  actionpack (4.2.0) lib/action_controller/metal/rendering.rb:10:in `process_action'
  actionpack (4.2.0) lib/abstract_controller/callbacks.rb:20:in `block in process_action'
  activesupport (4.2.0) lib/active_support/callbacks.rb:117:in `call'
  activesupport (4.2.0) lib/active_support/callbacks.rb:169:in `block in halting'
  activesupport (4.2.0) lib/active_support/callbacks.rb:169:in `block in halting'
  activesupport (4.2.0) lib/active_support/callbacks.rb:92:in `_run_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:734:in `_run_process_action_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:81:in `run_callbacks'
  actionpack (4.2.0) lib/abstract_controller/callbacks.rb:19:in `process_action'
  actionpack (4.2.0) lib/action_controller/metal/rescue.rb:29:in `process_action'
  actionpack (4.2.0) lib/action_controller/metal/instrumentation.rb:31:in `block in process_action'
  activesupport (4.2.0) lib/active_support/notifications.rb:164:in `block in instrument'
  activesupport (4.2.0) lib/active_support/notifications/instrumenter.rb:20:in `instrument'
  activesupport (4.2.0) lib/active_support/notifications.rb:164:in `instrument'
  actionpack (4.2.0) lib/action_controller/metal/instrumentation.rb:30:in `process_action'
  activerecord (4.2.0) lib/active_record/railties/controller_runtime.rb:18:in `process_action'
  actionpack (4.2.0) lib/abstract_controller/base.rb:137:in `process'
  actionview (4.2.0) lib/action_view/rendering.rb:30:in `process'
  actionpack (4.2.0) lib/action_controller/metal.rb:195:in `dispatch'
  actionpack (4.2.0) lib/action_controller/metal/rack_delegation.rb:13:in `dispatch'
  actionpack (4.2.0) lib/action_controller/metal.rb:236:in `block in action'
  actionpack (4.2.0) lib/action_dispatch/routing/route_set.rb:73:in `dispatch'
  actionpack (4.2.0) lib/action_dispatch/routing/route_set.rb:42:in `serve'
  actionpack (4.2.0) lib/action_dispatch/journey/router.rb:43:in `block in serve'
  actionpack (4.2.0) lib/action_dispatch/journey/router.rb:30:in `serve'
  actionpack (4.2.0) lib/action_dispatch/routing/route_set.rb:802:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/rack/agent_hooks.rb:26:in `traced_call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:55:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/rack/browser_monitoring.rb:23:in `traced_call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:55:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/rack/developer_mode.rb:56:in `traced_call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:55:in `call'
  rack (1.6.0) lib/rack/etag.rb:24:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rack (1.6.0) lib/rack/conditionalget.rb:25:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rack (1.6.0) lib/rack/head.rb:13:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/params_parser.rb:27:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  activerecord (4.2.0) lib/active_record/query_cache.rb:36:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  activerecord (4.2.0) lib/active_record/connection_adapters/abstract/connection_pool.rb:647:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  activerecord (4.2.0) lib/active_record/migration.rb:378:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
  activesupport (4.2.0) lib/active_support/callbacks.rb:88:in `_run_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:734:in `_run_call_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:81:in `run_callbacks'
  actionpack (4.2.0) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/reloader.rb:73:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/remote_ip.rb:78:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  appsignal (0.11.0) lib/appsignal/rack/listener.rb:13:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  airbrake (4.1.0) lib/airbrake/rails/middleware.rb:13:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  better_errors (2.0.0) lib/better_errors/middleware.rb:84:in `protected_app_call'
  better_errors (2.0.0) lib/better_errors/middleware.rb:79:in `better_errors_call'
  better_errors (2.0.0) lib/better_errors/middleware.rb:57:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  railties (4.2.0) lib/rails/rack/logger.rb:38:in `call_app'
  railties (4.2.0) lib/rails/rack/logger.rb:20:in `block in call'
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:68:in `block in tagged'
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:26:in `tagged'
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:68:in `tagged'
  railties (4.2.0) lib/rails/rack/logger.rb:20:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/request_id.rb:21:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rack (1.6.0) lib/rack/runtime.rb:18:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rocket_pants (1.10.0) lib/rocket_pants/cache_middleware.rb:21:in `_call'
  rocket_pants (1.10.0) lib/rocket_pants/cache_middleware.rb:12:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  activesupport (4.2.0) lib/active_support/cache/strategy/local_cache_middleware.rb:28:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rack (1.6.0) lib/rack/lock.rb:17:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/static.rb:113:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rack-cors (0.3.1) lib/rack/cors.rb:72:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  airbrake (4.1.0) lib/airbrake/user_informer.rb:16:in `_call'
  airbrake (4.1.0) lib/airbrake/user_informer.rb:12:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  hirefire-resource (0.3.4) lib/hirefire/middleware.rb:29:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  railties (4.2.0) lib/rails/engine.rb:518:in `call'
  railties (4.2.0) lib/rails/application.rb:164:in `call'
  newrelic_rpm (3.9.7.266) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in `call'
  rack (1.6.0) lib/rack/content_length.rb:15:in `call'
  puma (2.9.2) lib/puma/server.rb:490:in `handle_request'
  puma (2.9.2) lib/puma/server.rb:361:in `process_client'
  puma (2.9.2) lib/puma/server.rb:254:in `block in run'
  puma (2.9.2) lib/puma/thread_pool.rb:92:in `block in spawn_thread'

@apotonick
Copy link
Member

I hate Rails.

@apotonick
Copy link
Member

What roar-rails version are you on?

@chadwtaylor
Copy link
Contributor Author

roar 1.0.0
roar-rails 1.0.1
rails 4.2.0

I love Rails. :)

@apotonick
Copy link
Member

That's because you haven't seen how simple things can be with less Rails: https://leanpub.com/trailblazer

@chadwtaylor
Copy link
Contributor Author

I'm pretty sure -- the thing is we're knee-deep into a Rails project... so we can't switch boats just yet. :(

@apotonick
Copy link
Member

I hear ya! 😜

Trailblazer is Rails, just less coupling to the actual framework and less weirdness going on in controllers and models.

I really have no idea what's going on on your side - I checked an tests pass with Rails 4.2 and roar-rails. Are you using rails-api?

@apotonick
Copy link
Member

AH, I know what's your problem.

You got a file app/views/v2/peoples/index.html.haml and that makes it try to call render_default and this is exactly why I hate Rails.

Explanation: Rails and its responders use exceptions to find out whether or not a template is present and then base further behavior on that. And that is just wrong as it is completely hidden semantic, but, hey, magic, yeah, FTW! 😛

@chadwtaylor
Copy link
Contributor Author

Correct, am using rails-api and the file index.html.haml doesn't even exist... this is so weird. Yea, FTW, will continue to play around and get something going! :)

Thanks!

@apotonick
Copy link
Member

Are there any files in your app/views? See, this is where the wrong flow starts: https://github.com/plataformatec/responders/blob/master/lib/action_controller/responder.rb#L190

Wait, what was your actual problem in the first place? We can make that work and you keep using render ?

@chadwtaylor
Copy link
Contributor Author

Yes, I have views but they're for mailers only (ie: app/view/person_mailer).

My issue was trying to keep one representer for each models (rather than creating two for each model like PersonRepresenter and PeopleRepresenter).

I even created a PeopleRepresenter and used render, it found the PeopleRepresenter file but I got this error: undefined method 'id' for #<Array:0x007fe3277c3ac8>

I wonder though if the people_representer.rb is supposed to have a certain implementation? I basically copied-pasted-renamed from person_representer.rb to people_representer.rb.

@apotonick
Copy link
Member

It sounds like your representers for singular models are ok. However, you need to define a representer for collections, too (or use represent_items_with, which isn't implemented for render, YET cough wink).

@chadwtaylor
Copy link
Contributor Author

Do you have a sample of what a representer for collections should look like?

Yea, represent_items_with for render would be awesome… one day! :)

@apotonick
Copy link
Member

Tons of examples in issues, check Lonely Collections. Or buy my book and wait a few weeks until we get to the document API parts. _cough_ _COUGH_*

@chadwtaylor
Copy link
Contributor Author

:)

Ah, thanks for the Lonely Collection tip -- I overlooked that!

Now things are rendering for collections high five and there's a but... Here's what the result looked like:

[
  {
    "people":{
      "id":8,
      "first_name":"Jane",
      "last_name":"Doe",
      "full_name":"Jane Doe",
    }
  },
  {
    "people":{
      "id":18,
      "first_name":"John",
      "last_name":"Doe",
      "full_name":"John Doe",
    }
  }
]

I would think it should look like this...

{
  people: [
    {
      id: 8,
      first_name: "Jane",
      last_name: "Doe",
      full_name: "Jane Doe",
    },
    {
      id: 18,
      first_name: "John",
      last_name: "Doe",
      full_name: "John Doe",
    }
  ] 
}

Here's what my PeopleRepresenter looks like:

module Api
  module V2
    class PeopleRepresenter < Roar::Decorator
      include Representable::JSON::Collection
      items extend:Api::V2::PersonRepresenter, class:Person
    end
  end
end

@apotonick
Copy link
Member

Your PersonRepresenter must have a representation_wrap set?

@chadwtaylor
Copy link
Contributor Author

Ah, thanks... just added self.represenation_wrap = :people and we are so close... Here's the new JSON payload:

{
  people: [
    {
      "people":{
        "id":8,
        "first_name":"Jane",
        "last_name":"Doe",
        "full_name":"Jane Doe",
      }
    },
    {
      "people":{
        "id":18,
        "first_name":"John",
        "last_name":"Doe",
        "full_name":"John Doe",
      }
    }
  ]
}

@apotonick
Copy link
Member

Haha, no, I am saying Api::V2::PersonRepresenter should NOT have a representation wrap set, otherwise it will add it for every item. Paste Api::V2::PersonRepresenter.

@chadwtaylor
Copy link
Contributor Author

(will paste soon; BTW just bought your book....)

@apotonick apotonick reopened this Feb 4, 2015
@chadwtaylor
Copy link
Contributor Author

I commented out type :people in PersonRepresenter and now the collection is rendering the JSON payload correctly.

However, I need to keep type :people for a single JSON payload, what to do?

Here's the person_representer.rb content:

module Api
  module V2
    class PersonRepresenter < Roar::Decorator
      include Roar::JSON::JSONAPI

      # type :people # commented that line to make PeopleRepresenter wrap correctly; but need this for a single model, what to do?

      property :id
      property :first_name
      property :last_name
      property :full_name

    end
  end
end

@chadwtaylor
Copy link
Contributor Author

We are almost there... the current issue is that the type has to be in PersonRepresenter for single models to show:

{
  people: {
    id: 8,
    first_name: "Jane",
    ...
  }
}

Am I making sense?

So close; and then I'll be able to continue my merry way in building an Ember.js frontend.

Thanks so much for your patience and support!

@chadwtaylor
Copy link
Contributor Author

Update: As a temporary unfavorable workaround was implementing three files:

PersonRepresenter - this has type :people
PersonUnwrapRepresenter - a copy of PersonRepresenter sans type
PeopleRepresenter - will call PersonUnwrapRepresenter instead.

Obviously not a good practice but that's something I could get some work done while waiting to see what you have to say.

BTW, loved your intro in your book -- the Rails date! I first used it in 2008 so you're two years ahead of me.

@chadwtaylor
Copy link
Contributor Author

Just in case, I'm sharing my current implementations:

PersonRepresenter:

module Api
  module V2

    class PersonRepresenter < Roar::Decorator 
      include Roar::JSON::JSONAPI

      type :people

      property :uuid, as: :id
      property :first_name
      property :last_name
      property :full_name

    end

  end
end

PersonUnwrapRepresenter:

module Api
  module V2

    class PersonRepresenter < Roar::Decorator 
      include Roar::JSON::JSONAPI

      property :uuid, as: :id
      property :first_name
      property :last_name
      property :full_name

    end

  end
end

PeopleRepresenter:

module Api
  module V2
    class PeopleRepresenter < Roar::Decorator 
      include Representable::JSON::Collection
      self.representation_wrap = :people
      items extend: Api::V2::PersonUnwrapRepresenter, class:Person
    end
  end
end

Api::V2::PeopleController:

module V2
  class Api::V2::PeopleController < Api::V2::ApiController
    include Roar::Rails::ControllerAdditions
    include Roar::Rails::ControllerAdditions::Render

    # GET /people
    def index
      p = Person.first # single model
      # p = Person.find([8,18]) # collection model
      render json:p
    end

  end
end

@apotonick
Copy link
Member

Maaan I didn't realize me you're using JSONAPI - would have helped! 😆

Note that you need only ONE representer for both singular and collection. Please, check out if rendering works manually as documented here.

Then, we need to extend roar-rails to do the same. Rendering/parsing collection is not implemented, yet.

@chadwtaylor
Copy link
Contributor Author

So the manual rendering worked! See my irb output below:

irb(main):006:0> Api::V2::PersonRepresenter.prepare(Person.first).to_json
  Person Load (0.3ms)  SELECT  "people".* FROM "people"  ORDER BY "people"."id" ASC LIMIT 1
=> "{\"people\":{\"id\":\"dab4e5e6-ba50-42b4-9633-50a952d2e811\",\"first_name\":\"Chad\",\"last_name\":\"Taylor\",\"full_name\":\"Chad Taylor\"}}"

irb(main):008:0* Api::V2::PersonRepresenter.for_collection.prepare([Person.first, Person.last]).to_json
  Person Load (0.5ms)  SELECT  "people".* FROM "people"  ORDER BY "people"."id" ASC LIMIT 1
  Person Load (0.9ms)  SELECT  "people".* FROM "people"  ORDER BY "people"."id" DESC LIMIT 1
=> "[{\"people\":{\"id\":\"dab4e5e6-ba50-42b4-9633-50a952d2e811\",\"first_name\":\"Chad\",\"last_name\":\"Taylor\",\"full_name\":\"Chad Taylor\"}},{\"people\":{\"id\":\"265abc1c-ef97-485b-b43d-13231108f4ab\",\"first_name\":\"\",\"last_name\":\"John Doe\",\"full_name\":\" John Doe\"}}]"

What do I need to do next? Extending roar-rails; how? Or is it something you need to do an update within the gem before it can function properly?

@apotonick
Copy link
Member

Ha of course manual rendering works! 😛

So far, roar-rails doesn't know about the ::for_collection method, we have to implement that. Do we have tests for JSONAPI, yet? Let me have a look.

@chadwtaylor
Copy link
Contributor Author

Cool, let me know what I can do next if anything?

@apotonick
Copy link
Member

Check out this test: https://github.com/apotonick/roar-rails/blob/master/test/json_api_renderer_test.rb

You could add a test to render a collection here. This will fail, then we can easily fix it in roar-rails and people all over the world are gonna love you.

@chadwtaylor
Copy link
Contributor Author

So this is the first time I have done a test (I know, I know, my peers have lectured me about that already).

Here's what I have:

require 'test_helper'

class HalRendererTest < ActionController::TestCase
  include Roar::Rails::TestCase

  class PeopleController < ActionController::Base
    module JsonApiPersonRepresenter
      include Roar::JSON::JSONAPI
      type :people
      property :first_name
    end

    include Roar::Rails::ControllerAdditions

    represents :json_api, :entity => JsonApiPersonRepresenter

    def show
      person = Person.find 1
      respond_with person
    end

    def index
      people = Person.find([1,2])
      respond_with people
    end

  end

  tests PeopleController

  test "should render single model correctly in response to a application/vnd.api+json" do
    get :show, :id => "1", :format => :json_api
    assert_body '{"people":{"first_name":"Chad"}}'
  end

  test "should have a content_type of application/vnd.api+json for a single model" do
    get :show, :id => "bumi", :format => :json_api
    assert_equal response.content_type, 'application/vnd.api+json'
  end

  test "should render collection of models correctly in response to a application/vnd.api+json" do
    get :index, :format => :json_api
    assert_body '{"people":[{"first_name":"Chad"},{"first_name":"Fremont"}]}'
  end

  test "should have a content_type of application/vnd.api+json for a collection of models" do
    get :index, :format => :json_api
    assert_equal response.content_type, 'application/vnd.api+json'
  end

end

I have already added this to config/initializers/mime_types.rb:

ActionController::Renderers.add :json_api do |obj, options|
  self.content_type ||= Mime[:json_api]
  obj
end

Running the test gave us errors:

Finished in 0.077660s, 51.5066 runs/s, 0.0000 assertions/s.

  1) Error:
HalRendererTest#test_should_have_a_content_type_of_application/vnd.api+json_for_a_collection_of_models:
NameError: uninitialized constant Mime::JSON_API
    test/models/json_api_renderer_test.rb:24:in `index'
    test/models/json_api_renderer_test.rb:47:in `block in <class:HalRendererTest>'


  2) Error:
HalRendererTest#test_should_have_a_content_type_of_application/vnd.api+json_for_a_single_model:
NameError: uninitialized constant Mime::JSON_API
    test/models/json_api_renderer_test.rb:19:in `show'
    test/models/json_api_renderer_test.rb:37:in `block in <class:HalRendererTest>'


  3) Error:
HalRendererTest#test_should_render_collection_of_models_correctly_in_response_to_a_application/vnd.api+json:
NameError: uninitialized constant Mime::JSON_API
    test/models/json_api_renderer_test.rb:24:in `index'
    test/models/json_api_renderer_test.rb:42:in `block in <class:HalRendererTest>'


  4) Error:
HalRendererTest#test_should_render_single_model_correctly_in_response_to_a_application/vnd.api+json:
NameError: uninitialized constant Mime::JSON_API
    test/models/json_api_renderer_test.rb:19:in `show'
    test/models/json_api_renderer_test.rb:32:in `block in <class:HalRendererTest>'

4 runs, 0 assertions, 0 failures, 4 errors, 0 skips

Error seems to be around Mime; any ideas?

@chadwtaylor
Copy link
Contributor Author

My bad, I forgot to add that line Mime::Type.register 'application/vnd.api+json', :json_api to the mime_types.rb file; oops!

Running the test, brb...

@chadwtaylor
Copy link
Contributor Author

New test result with mime type fixed:

Finished in 0.110748s, 36.1180 runs/s, 9.0295 assertions/s.

  1) Error:
HalRendererTest#test_should_have_a_content_type_of_application/vnd.api+json_for_a_collection_of_models:
NameError: uninitialized constant HalRendererTest::PeopleRepresenter
    test/models/json_api_renderer_test.rb:24:in `index'
    test/models/json_api_renderer_test.rb:47:in `block in <class:HalRendererTest>'


  2) Error:
HalRendererTest#test_should_render_collection_of_models_correctly_in_response_to_a_application/vnd.api+json:
NameError: uninitialized constant HalRendererTest::PeopleRepresenter
    test/models/json_api_renderer_test.rb:24:in `index'
    test/models/json_api_renderer_test.rb:42:in `block in <class:HalRendererTest>'


  3) Error:
HalRendererTest#test_should_render_single_model_correctly_in_response_to_a_application/vnd.api+json:
NoMethodError: undefined method `assert_body' for #<HalRendererTest:0x007fb7ab559028>
    test/models/json_api_renderer_test.rb:33:in `block in <class:HalRendererTest>'

4 runs, 1 assertions, 0 failures, 3 errors, 0 skips

@chadwtaylor
Copy link
Contributor Author

Sorry for bombing the thread here, I was able to fix the test and remembered you telling me about represent_items_with couple of days ago:

Here's the new test results:

Finished in 0.115252s, 34.7066 runs/s, 17.3533 assertions/s.

  1) Error:
HalRendererTest#test_should_render_collection_of_models_correctly_in_response_to_a_application/vnd.api+json:
NoMethodError: undefined method `assert_body' for #<HalRendererTest:0x007fd764db81c8>
    test/models/json_api_renderer_test.rb:43:in `block in <class:HalRendererTest>'


  2) Error:
HalRendererTest#test_should_render_single_model_correctly_in_response_to_a_application/vnd.api+json:
NoMethodError: undefined method `assert_body' for #<HalRendererTest:0x007fd764d2b458>
    test/models/json_api_renderer_test.rb:33:in `block in <class:HalRendererTest>'

4 runs, 2 assertions, 0 failures, 2 errors, 0 skips

The corrected test file:

require 'test_helper'

class HalRendererTest < ActionController::TestCase
  include Roar::Rails::TestCase

  class PeopleController < ActionController::Base
    module PersonRepresenter
      include Roar::JSON::JSONAPI
      type :people
      property :first_name
    end

    include Roar::Rails::ControllerAdditions

    represents :json_api, :entity => PersonRepresenter

    def show
      person = Person.find 1
      respond_with person
    end

    def index
      people = Person.find([1,2])
      respond_with people, represent_items_with:PersonRepresenter
    end

  end

  tests PeopleController

  test "should render single model correctly in response to a application/vnd.api+json" do
    get :show, :id => "1", :format => :json_api
    assert_body '{"people":{"first_name":"Chad"}}'
  end

  test "should have a content_type of application/vnd.api+json for a single model" do
    get :show, :id => "bumi", :format => :json_api
    assert_equal response.content_type, 'application/vnd.api+json'
  end

  test "should render collection of models correctly in response to a application/vnd.api+json" do
    get :index, :format => :json_api
    assert_body '{"people":[{"first_name":"Chad"},{"first_name":"Fremont"}]}'
  end

  test "should have a content_type of application/vnd.api+json for a collection of models" do
    get :index, :format => :json_api
    assert_equal response.content_type, 'application/vnd.api+json'
  end

end

@chadwtaylor
Copy link
Contributor Author

So it didn't look like it liked "assert_body" as was in the original test file you sent me. So I adjusted it:

  test "should render single model correctly in response to a application/vnd.api+json" do
    get :show, :id => "1", :format => :json_api
    # assert_body '{"people":{"first_name":"Chad"}}'
    assert_equal response.body, '{"people":{"first_name":"Chad"}}'
  end

  test "should render collection of models correctly in response to a application/vnd.api+json" do
    get :index, :format => :json_api
    # assert_body '{"people":[{"first_name":"Chad"},{"first_name":"Fremont"}]}'
    assert_equal response.body, '{"people":[{"first_name":"Chad"},{"first_name":"Fremont"}]}'
  end

And the new error is:

Finished in 0.113740s, 35.1679 runs/s, 35.1679 assertions/s.

  1) Failure:
HalRendererTest#test_should_render_collection_of_models_correctly_in_response_to_a_application/vnd.api+json [/Users/chadwtaylor/Dropbox/web_projects/honey/linguabee-api/test/models/json_api_renderer_test.rb:46]:
--- expected
+++ actual
@@ -1 +1 @@
-"{\"people\"=>{\"first_name\"=>\"Chad\"}}{\"people\"=>{\"first_name\"=>\"Fremont\"}}"
+"{\"people\":[{\"first_name\":\"Chad\"},{\"first_name\":\"Fremont\"}]}"

4 runs, 4 assertions, 1 failures, 0 errors, 0 skips

I'm hoping that is the specific failure you wanted to see? :)

@apotonick
Copy link
Member

Any chance I could see a PR so I can diff? Thanks brother, legend!

@chadwtaylor
Copy link
Contributor Author

Pardon my ignorance as I have never become a contributor for any projects -- so you wanted a PR on my test file?

@chadwtaylor
Copy link
Contributor Author

Sent you a PR of my test; hoping I got this right...

@chadwtaylor
Copy link
Contributor Author

Let me know what I can do for you. Would love to help!

Friendly reminder... I can only use render as respond_with throws a undefined method 'default_render' error as discussed way above in this comment thread (am using the rails-api gem).

@apotonick
Copy link
Member

Friendly reminder: add test here: https://github.com/apotonick/roar-rails/blob/master/test/render_test.rb

Thanks for your help!

@chadwtaylor
Copy link
Contributor Author

On it right now. This one is for JSON. I will create another test for JSONAPI.

@apotonick
Copy link
Member

You don't have to create a test for JSONAPI and represent_items_with. JSONAPI has a well-defined behaviour for rendering collections which you can't override.

@chadwtaylor
Copy link
Contributor Author

Just sent you a PR.

The reason I wanted to do a JSONAPI test when using render is because the JSON payload is not publishing correctly on the response body. Unless the intent is for us to use resond_with in a JSONAPI situation?

@apotonick
Copy link
Member

Ah yeah, that's true, so in render we have to check if the representer supports for_collection.

@chadwtaylor
Copy link
Contributor Author

Will send you another PR in a bit.

@apotonick
Copy link
Member

@chadwtaylor
Copy link
Contributor Author

Awesome, my first contributions! :)

So, as you can see, the use of render and Roar::JSON::JSONAPI is not working out for me as my recent PR will show that.

Instead, I am currently using Roar::JSON and wrap things up using represenation_wrap to be JSONAPI-compatible for my Ember.js frontend project. Not the best solution so I'm looking forward to when my rails-api can utilize Roar::JSON::JSONAPI using respond_with.

Do you think we will see that day? I really, really hope so.... begging, begging

@chadwtaylor
Copy link
Contributor Author

It's bed time here for me on this side of earth. :) So if there's anything else you'd like me to do, feel free to holler!

Would love to know your thoughts on a rails-api utilizing JSON and respond_with, would that be possible?

@apotonick
Copy link
Member

Absolutely, let's make the respond_with + representers happen. Open a new issue? This one is becoming a bit, ehm, ...long?

@hilary
Copy link

hilary commented Apr 9, 2015

hey @apotonick, where does making respond_with + representers stand? I just tried it and it doesn't seem to be working, even after reading through the thread.

I'd be happy to pick up where ya'll left off if you fill me in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants