I am building a Rails api and currently have this folder structure:
The error_serializer.rb file is a module:
module ErrorSerializer
extend ActiveSupport::Concern
...methods here...
end
Which I can include in any of the api controllers, for example:
class Api::TemplatesController < ApiController
include ErrorSerializer
...
end
But since this errors_serializer module is only relevant to api controllers, I want to move the file to 'api/concerns/error_serializer.rb'.
But that generates the error:
ActionController::RoutingError (uninitialized constant Api::TemplatesController::ErrorSerializer)
I tried changing the name inside the file to:
module Api::ErrorSerialzer
but got the same error.
So what must I change to be able to move that file?
Since rails expects your module naming to follow your file structure, your concern should be named:
module Api::Concerns::ErrorSerializer
Since you're including it in Api::TemplatesController, I would do:
class Api::TemplatesController < ApiController
include Api::Concerns::ErrorSerializer
...
end
To help rails out with the constant lookup.
Thanks to the answer from #jvillian and this blog post, I was able to figure out the 'Rails' way to do this (since actually I will need the concern in all Api controllers, and also my api controller was outside the api namespace). So I'm posting this solution as (I think) it's the preferred way:
I moved the error_serialzier.rb file into api/concerns and change the code to include the Api namespace:
module Api::Concerns::ErrorSerializer
extend ActiveSupport::Concern
...
end
I also moved api_controller.rb file and put it inside the /api folder, and thus into the API module namespace, so now it looks like this:
class Api::ApiController < ActionController::API
before_action :authenticate_api_user!
include DeviseTokenAuth::Concerns::SetUserByToken
include Concerns::ErrorSerializer
respond_to :json
end
This got rid of the uninitialized constant errors.
Related
I have a module ApplicationHelper in app/helpers/application_helper.rb
defined like this
module ApplicationHelper
def some_method(arg)
end
end
and i have my view file is here
app/views/v1/admin/messages/show.json.jbuilder
So i am trying to access the
some_method()
in view file but it doesn't reflect!
Is this due to namespacing? or what i am not able to understand.
It would be Great if someone explains the concept.
Thanks in advance!
it says undefined method error
what could be the reason?
You didn't include your controller code, but we'll assume it ultimately inherits from ActionController::API (as it should it if it is an API controller). If so, that is the root of it rather than namespacing, etc. Per the ActionController documentation:
An API Controller is different from a normal controller in the sense
that by default it doesn't include a number of features that are
usually required by browser access only: layouts and templates
rendering, flash, assets, and so on. This makes the entire controller
stack thinner, suitable for API applications. It doesn't mean you
won't have such features if you need them: they're all available for
you to include in your application, they're just not part of the
default API controller stack.
One of the side effects of the thinner API controller is that they don't automatically include helpers like a standard Rails controller. You can easily add that back in, though.
messages_controller.rb
class Api::V1::MessagesController < ActionController::API
include ActionController::Helpers
helper ApplicationHelper
def show
# whatever
end
end
app/helpers/application_helper.rb
module MessagesHelper
def some_method(arg)
# whatever
end
end
app/views/messages/show.json.jbuilder
json.bogus do
thing1: some_method('banana')
end
If you have lots of API controllers, you can of course stick it in a base controller class they all inherit from like so:
class Api::V1::ApiController < ActionController::API
include ActionController::Helpers
helper ApplicationHelper
end
In Rails, how do you use a specific method from a module. For eg,
# ./app/controllers/my_controller.rb
class MyController < ApplicationController
include MyModule
def action
MyModule.a_method
end
private
def a_method
...
end
end
# ------------------------------------------------ #
# ./app/helpers/my_module.rb
module MyModule
def a_method
...
end
end
MyController includes MyModule. And in action ,I want to use MyModule.a_method (Please note I also have a private a_method in MyController and I don't want to use this.)
Things I've tried :
1) Defining the method in the module as self.
def self.a_method
end
2) Using the :: notation in controller (MyModule::a_method)
The error that I keep getting is
Undefined method:a_method for MyModule:module
For now, I've resorted to using a different name for the modules method. But I'd like to know how to namespace the function with either the Module:: or Module. notation
[UPDATE - 11/24/2014]
adding file structure in code, since Rails heavily relies on convention.
So I am not really sure what you are trying to accomplish with your module but a quick solution to get it working is below.
Move my_module.rb out of helpers and into lib/my_module.rb. The helpers directory is for methods that you use in your views. The convention is to utilize helpers that are namespaced after their respective controller or the application_helper.rb for global methods for your views. Not sure if that's what you are trying to accomplish with your module but wanted to throw that out there.
Create an initializer (you can all it whatever) in config/initializers/custom_modules.rb and add require 'my_module'
Update the a_method back to be self.a_method
You can now call MyModule.a_method in your app
Don't forget to restart your server for changes to lib/my_module.rb to take effect.
Also, a lot of people reference this post by Yehuda Katz as guidance on where to store code for your app. Thought it might be a helpful reference.
if you include MyModule into MyController, all the "instance methods" of the first will be mixed-in into the 2nd.
So if you only want to call MyModule.a_method, no need to include your module.
Then you'd want to require (or better autoload) your module before using it. To do so place it in controllers/concerns/my_module.rb, rails (4 at least) should autoload it, otherwise require its file in an intializer
# my_module.rb
module MyModule
def self.a_method
...
end
end
should work, but doing
# my_module.rb
module MyModule
extend self
def a_method
...
end
end
is more clean to me. You'd like to have a look to rails active support concern to understand the "rails way" on this topic.
If I do:
rails generate scaffold account/user username
I get a controller that looks like this:
class Account::UsersController < ApplicationController
def index
#account_users = Account::User.all
end
...
end
If I include the Account Module, then it looks like all the database calls don't need to be prefixed with "Account::". I.e.
class Account::UsersController < ApplicationController
include Account
def index
#account_users = User.all #this works because I included the Account Module above
end
...
end
Now if I were to move my
controllers/account/users_controller.rb
file to:
controllers/admin/account/users_controller.rb
The file looks like this (note: I also corrected my routes file after this move):
class Admin::Account::UsersController < ApplicationController
include Account
def index
#account_users = User.all #this call does not work now
end
...
end
But I get an error saying "uninitialized constant Admin::Account::UsersController::User"
It looks like rails is trying to make a database call on the "User" model without the "Account::" module in front of it.
So how does including modules in controllers work? Why does this not work when I move my controller into a different file (and leave the model in the same location from the generated scaffold) but it works with the scaffold generated files? How can I fix this issue?
Resolving the name of a module is done relative to the current module. Try and change it to:
include ::Account
or
include ::Admin::Account
(depending on the module in which your User model is defined)
This will tell ruby to look in the global namespace for the module Account
I guess I didn't realize you can just explicitly require the path to the module you would like to include. I learned this after reading up on modules some more...
So adding an explicit call to "require 'account/user'" just outside the controller class makes it so including the module in the controller works.
I have a method defined in application_helper.rb:
def bayarea_cities
[
['San Francisco', 'San Francisco'],
['Berkeley', 'Berkeley'],
...
]
end
I'm also using Grape to create an API. It's in its own module outside the Rails app:
module FFREST
class API_V2 < Grape::API
...
I'm pretty sure Grape is a Rack app, so it doesn't have normal access to the Rails modules. When I try to call the 'bayarea_cities' method in one of the API methods, I get an undefined variable or method error. I've tried include the ApplicationHelper module with 'include ApplicationHelper', but this did not work.
How can I get access to this inside the API class?
UPDATE:
Thanks for the update Deefour. I added extend self to my Helpers module, and referenced the methods as instance/mixin methods (not as module methods), but I'm still getting the same error. In my lib/helpers.rb file I have:
module Helpers
extend self
def bayarea_cities
[
'San Francisco',
'Berkeley',
'Danville',
'Oakland',
'Daly City',
'Sunnyvale'
]
end
def us_states
['CA']
end
end
and in my API file I have:
module FFREST
class API_V1 < Grape::API
include Helpers
version 'v1', :using => :header, :vendor => 'feedingforward'
...
And of course, I have the config/initializers/helpers.rb file that says require "helpers"
But when I call the US states API method, for instance, by going to http://localhost:5000/api/states, I get:
undefined local variable or method `us_states' for #<Grape::Endpoint:0x007fd9d1ccf008>
Any ideas?
Create some lib/helpers.rb file with the contents: module Helpers; end
Move the bayarea_cities method into this module definition
Add a config/initializers/helpers.rb file containing require "helpers"
Inside the ApplicationHelpers class, add include Helpers
Inside your API_V2 class add include Helpers
You'll now have told Rails to make the Helpers module available within your application, and made bayarea_cities available as a method within both your Grape API class and your Rails app. The above are steps simply to get the point across - you need to put this common functionality in a place it can be easily accessed by any part of your application. You can (and should) use namespace your Helpers module.
Another tip: add extend self to the module to avoid the need to define everything as class methods as you mentioned in the comment
module Helpers
extend self
def bayarea_cities
#...
end
end
Finally, if you're including the module properly with include Helpers, you should be able to acces the method simply as bayarea_cities, not Helpers.bayarea_cities. If this isn't the case, you should definitely show the error you get so we can sort that out for you.
I am working on a rails engine and I have a problem with the helpers.
Apparently this is a known "problem" but there's not a lot of solutions out there. The problem is that I have an AuthenticationHelper which I want to access globally - but it's not working.
I've read that you could add a few lines to your init.rb but it does not seem to have any effect.
Any idea what the best way to make an application available in an engine?
EDIT: Fixed it- Just put the code (from the link) in the engine.rb instead.
Put this code in engine.rb:
config.to_prepare do
ApplicationController.helper(MyEngineHelper)
end
To access main app helpers (ApplicationHelper) from engine's views I tried include this:
app/helpers/your_engine/application_helper.rb
module YourEngine
module ApplicationHelper
include ActionView::Helpers::ApplicationHelper
end
end
It works, but once, when I restarted dev server, it throws me uninitialized constant ActionView::Helpers::ApplicationHelper, but I can't reproduce this exception.
EDIT
Removed this include and made this one:
lib/my_engine/engine.rb (it's inside engine)
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
config.to_prepare do
ApplicationController.helper(ActionView::Helpers::ApplicationHelper)
end
end
end
Adding this just in case:
I had the same problem using the Administrate gem with Rails 7. I wanted to access my main app helper modules.
Simply adding helper all_helpers_from_path 'app/helpers' in Admin::ApplicationController solved this. You can find the official documentation here.
My file now looks like this:
module Admin
class ApplicationController < Administrate::ApplicationController
before_action :authenticate_admin_user!
helper all_helpers_from_path "app/helpers"
end
end
I found the answer here.