I'm trying to use JSONAPI Resources in a Rails engine and I've defined DokiCore::Tenant (the model) in doki_core/app/models/tenant.rb and DokiCore::TenantResource in doki_core/app/resources/tenant_resource.rb. When I attempt to serialize to hash, I encounter the following error:
NoMethodError: undefined method tenant_path' for #<Module:0x007f9d04208778>
from /Users/typeoneerror/.rvm/gems/ruby-2.2.2#doki/gems/jsonapi-resources-0.6.1/lib/jsonapi/link_builder.rb:77:inpublic_send'
The resource uses model_name to let it know where the model actually is:
module DokiCore
class TenantResource < JSONAPI::Resource
model_name 'DokiCore::Tenant'
# ...
end
end
I'm trying to output the hash for a tenant like so:
tenant = DokiCore::Tenant.find(1);
resource = DokiCore::TenantResource.new(tenant, nil);
serializer = JSONAPI::ResourceSerializer.new(DokiCore::TenantResource);
serializer.serialize_to_hash(resource);
which is where the error happens.
How can I get the links to work correctly and/or disable them? I assume this is there it adds the URL to the resource as a link under the "links" key in the outputted json.
Sorted this out. If your routes are namespaced in any way, your resources also need to be namespaced to match. My routes look something like:
namespace :api do
namespace :v1 do
resources :tenants
end
end
So the resource needs to be namespaced the same way:
tenant = DokiCore::Tenant.find(1);
resource = DokiCore::API::V1::TenantResource.new(tenant, nil);
serializer = JSONAPI::ResourceSerializer.new(DokiCore::API::V1::TenantResource);
serializer.serialize_to_hash(resource);
Another simple way to serialize your namespaced data is by using the jsonapi-utils gem. You will just need to do something like this:
class API::V1::UsersController < API::V1::BaseController
def index
jsonapi_render json: User.all
end
end
The gem is based on JSON API Resources, bringing a Rails way to get data serialized with JSON API's specs.
Related
I'm running into the infamous No Method Error. I've worked my way through a number of examples here on STOF but I can't see an error in my code that stands out. I've checked that rake routes matches what I think should be happening and the paths provided from using resources in the routes.db file seem to be correct. I know I'm missing some small detail but I can't for the life of me see it now. Any help would be appreciated.
My Controller code:
class GenevarecordsController < ApplicationController
def index
#genevarecords = GenevaRecord.all.page(params[:page]).per(5)
end
def new
#genevarecord = GenevaRecord.new
end
end
My routes:
Rails.application.routes.draw do
root 'genevarecords#index'
resources :genevarecords
end
You have a naming discrepency between your model and your controller / routes.
your model is GenevaRecord, underscored makes it geneva_record. However your controller only has a single capital letter at the beginning: Geneverecords which underscored would be genevarecords. Therefore when you pass your model to the form it tries to use a controller / routes helpers with the same naming format as the model, which would be geneva_records_controller ie. GenevaRecordsController.
What you need to do is match your controller and routes to the same naming format as your model:
class GenevaRecordsController < ApplicationController
#...
end
Rails.application.routes.draw do
#...
resources :geneva_records
end
You need to take Did you mean? section seriously,
Anyway, if you closely look at ruby syntax following is the representation for the class name,
AbcDef and equivalent snake case is abc_def
In your case,
Your model is named as GenevaRecord but your controller is GenevarecordsController
change it to GenevaRecordsController, also you need to match it's equivalent snake case in routes...
Rails.application.routes.draw do
root 'geneva_records#index'
resources :geneva_records
end
So, when you pass #genevarecord to the form it is initialized as GenevaRecord.new and searches for geneva_records_path which is undefined because you have defined it as genevarecords_path which doesn't match you model (resources)..
Hope it helps in understanding..
I have a routes file that looks something like this (It's very deeply nested...I know):
scope :admin, module: :admin do
namespace :breadth do
resources :areas, as: 'areas' do
resources :sequences, as: 'sequences'
end
end
end
When running rake routes I get a back all of the routes including one called breatdh_area_sequences (which is exactly what I want). The problem is that when I create a form rails builds up the wrong url based on the parameters I'm giving it:
= form_for [:breadth, breadth_sequence.area, breadth_sequence] do |f|
...
This gives me:
undefined method `breadth_breadth_area_breadth_sequences_path' for #<#<Class:0x007fe55bcf16b8>:0x007fe55d9a3f68>
Exactly how does rails take this array of parameters and create a URL path out of it? I'm assuming it calls a method on each object? Is this something I can override in order to get the named route I'm expecting (not overriding :url on form_for)
Since you want to name your objects differently from the routes, it won't work. Your routes/controllers need to be named just as your models are named, if your model is BreadthArea (as the route believes it is) your route should also be named in the same way.
In order to make this work I had to override the self.model_name methods on both BreadthArea and BreadthSequence. This method is how form_for figures out the URL when an object (or an array of objects) is used at the first argument.
class BreadthArea < ActiveRecord::Base
def self.model_name
ActiveModel::Name.new(self, nil, "Area")
end
...
end
class BreadthSequence < ActiveRecord::Base
def self.model_name
ActiveModel::Name.new(self, nil, "Sequence")
end
...
end
This is driving me crazy! I have the two models Lion and Cheetah. Both inherit from Wildcat.
class Wildcat < ActiveRecord::Base; end
class Lion < Wildcat; end
class Cheetah < Wildcat; end
STI is used here.
They all get handled through the controller WildcatsController. There, I have a before_filer to get the type of wildcat from the params[:type] and all the other stuff to use the correct class.
In my routes.rb, I created the following routes:
resources :lions, controller: 'wildcats', type: 'Lion'
resources :cheetahs, controller: 'wildcats', type: 'Cheetah'
If I now want to use the path helpers, that I get from the routes (lions_path,lion_path,new_lion_path, etc.), everything is working as expected, except the show and the new paths. For example lions_path returns the path /lions. The new path returns /lions/new?type=Lion. Same with the show path. When I try to enter /lions/new to my root domain it correctly adds the type param in the background.
So, my question is, why does Rails add the type parameter to the url if I use the path helper? And why only for new and show?
I am running Rails 4.0.0 with Ruby 2.0 using a fresh Rails app.
Why using type? Why not use inherited controllers?
resources :lions
resources :cheetahs
Then
class LionsController < WildCatsController
end
class CheetahController < WildCatsController
end
class WildCatsController < ApplicationController
before_filter :get_type
def index
#objs = #klass.scoped
end
def show
#obj = #klass.find(params[:id])
end
def new
#obj = #klass.new
end
# blah blah
def get_type
resource = request.path.split('/')[0]
#klass = resource.singularize.capitalize.constantize
end
I just had this problem. You could try to shutdown the server, remove the /tmp directory and restart.
Rails routes and controller parameters
So I want when I access: site.com/panel to look into /app/controller/panel/index_controller.rb
Before I start I'm new to ruby, I started a couple hours ago
So in my routes.rb I have this
namespace :panel do
root 'index#index'
resources :index
end
And I created a file called index_controller.rb in /app/controller/panel/index_controller.rb which looks like this
class IndexController < ApplicationController
def index
#foo = "Foo"
end
end
Now when I go to site.com/panel I get this: superclass mismatch for class IndexController
What I did wrong?
Also can I setup different views and layout here to use for the controllers inside /app/controller/panel/*_controller.rb
replace this
class IndexController < ApplicationController
with
class Panel::IndexController < ApplicationController
update:
to automatically generate namespaced controller you can use rails build in generator like this
rails g controller panel/users
this will generate Panel::Users < ApplicationController controller under app/controllers/panel/users_controller.rb
Since you've namespaced the index resource routes within panel, you'll need to prefix your IndexController declaration to reflect this:
# app/controllers/index_controller.rb
class Panel::IndexController < ApplicationController
Then, you can similarly reflect the namespace in your filesystem in order to get Rails to properly invoke the correct views:
/app/views/panel/index/index.html.erb
/app/views/panel/index/show.html.erb
... etc
A note: the Rails convention is that routes that are declared as resources should be named plural, as this denotes an entirely resourceful class. Thus, according to this paradigm, index should actually be indexes. However, I suspect you may mean to use a singular route, in which case the declaration would be as follows:
namespace :panel do
resource :index
end
Which creates the following singular routes (which may conform better to what you're trying to accomplish):
panel_index POST /panel/index(.:format) panel/indices#create
new_panel_index GET /panel/index/new(.:format) panel/indices#new
edit_panel_index GET /panel/index/edit(.:format) panel/indices#edit
GET /panel/index(.:format) panel/indices#show
PUT /panel/index(.:format) panel/indices#update
DELETE /panel/index(.:format) panel/indices#destroy
I'm writing an app where I need to override the default routing helpers for a model. So if I have a model named Model, with the corresponding helper model_path() which generates "/model/[id]". I'd like to override that helper to generate "/something/[model.name]". I know I can do this in a view helper, but is there a way to override it at the routing level?
You can define to_param on your model. It's return value is going to be used in generated URLs as the id.
class Thing
def to_param
name
end
end
The you can adapt your routes to scope your resource like so
scope "/something" do
resources :things
end
Alternatively, you could also use sub-resources is applicable.
Finally you need to adapt your controller as Thing.find(params[:id]) will not work obviously.
class ThingsController < ApplicationController
def show
#thing = Thing.where(:name => params[:id).first
end
end
You probably want to make sure that the name of your Thing is unique as you will observe strange things if it is not.
To save the hassle from implementing all of this yourself, you might also be interested in friendly_id which gives you this and some additional behavior (e.g. for using generated slugs)
You need the scope in routes.rb
scope "/something" do
resources :models
end