Closed chriskilding closed 12 years ago
Can you please include all relevant code (the controller in question, any presenters used, etc) and the full stack trace?
Ok, here's the code. He Who Pays The Bills insisted upon a certain level of obfuscation, but hopefully what you need is all there.
Controller:
module V1
class ItemsController < BaseController
def create
item = Item.new(params[:item])
item.save!
respond_with item
end
end
end
Next controller up:
module V1
class BaseController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource
end
end
Model:
class Item < ActiveRecord::Base
# the usual, nothing out of the ordinary here
end
routes.rb:
MyApp::Application.routes.draw do
api_version(module: "V1", path: "/v1") do
resources :items
... other unrelated resources...
end
root :to => 'welcome#index'
match '*path', to: 'welcome#index'
end
Stack trace:
Started POST "/v1/items" for 127.0.0.1 at 2012-07-23 14:30:55 +0100
Processing by V1::ItemsController#create as JSON
Parameters: {"authenticity_token"=>"some token", "item"=>{various correct params}
# bunch of SQL selects and inserts happen - the record creation does work, you can see it in the DB - and then...
Completed 500 Internal Server Error in 609ms
NoMethodError (undefined method `item_url' for #<V1::ItemsController:0x007fd686928d80>):
app/controllers/v1/items_controller.rb:36:in `create'
Rendered /Users/chris/.rvm/gems/ruby-1.9.3-p194@myapp/gems/actionpack-3.1.6/lib/action_dispatch/middleware/templates/rescues/_trace.erb (3.0ms)
Rendered /Users/chris/.rvm/gems/ruby-1.9.3-p194@myapp/gems/actionpack-3.1.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (1.0ms)
Rendered /Users/chris/.rvm/gems/ruby-1.9.3-p194@myapp/gems/actionpack-3.1.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (7.2ms)
No presenters were used.
This is enough to go on, thanks.
So it looks like your controller is namespaced V1, but your model is not, and that's the disconnect. This is exactly what Presenters are meant for. In most cases, your domain model spans multiple versions of your API, so you don't want to namespace the model classes, but instead you want to create namespaced Presenters which wrap your single domain model. This allows you to add/remove/change fields in the serialized representation of your models in each version of your API, without having to change the model itself.
For example:
# app/presenters/v1/item_presenter.rb
class V1::ItemPresenter < V1::BasePresenter
attr_accessor :item
def initialize(item)
@item = item
end
def as_json(options={})
# ...
end
def to_xml(options={}, &block)
# ...
end
end
Then your controller would change to look like the following:
module V1
class ItemsController < BaseController
def create
item = Item.new(params[:item])
item.save!
status = 200
response_body = V1::ItemPresenter.new(item)
respond_to do |format|
format.xml {render :xml => response_body.to_xml, :status => status}
format.json {render :json => response_body.as_json, :status => status}
end
end
end
I haven't actually tried using Presenters with respond_with, so your mileage may vary, but respond_to as used above definitely works.
Aha.... so that's what a presenter does.
I'll have a go building a presenter with respond_with and let you know how it goes. Cheers :)
Crap, on the create
action I get
Completed 500 Internal Server Error in 610ms
NoMethodError (undefined method `model_name' for V1::ItemPresenter:Class):
app/controllers/v1/items_controller.rb:36:in `create'
And the offending line in the create action is
respond_with V1::ItemPresenter.new(item)
I guess the presenter has to be beefed up with a few more methods that respond_with
needs.
Closing this issue as I don't think there's a bug here, but keep us posted if you get this working with respond_with
.
This issue here is that respond_with expects the method 'model_name' to be found on the presenter. It would be nice if this gem provided a presenter class to inherit from that would automatically forward this for us.
My controllers are set up to use respond_with so they can handle multiple formats easily.
However I am seeing that, on any actions that need to return a location, particularly POSTs and PUTs, I get an HTTP 500 back.
Under the hood, this is the exception:
The item is created successfully, but the respond_with call blows up with this unchecked exception which produces the 500.
Running rake routes says
so I appear to have a v1_item_url but not an item_url.
Is this something I've done wrong, or a bug in the way versionist does route namespacing / scoping?