I am setting up Grape on Rails 4.1. I am putting it inside lib according to http://funonrails.com/2014/03/building-restful-api-using-grape-in-rails/ .
I'd like to put helper methods in separated files, file structure is like following:
lib
|--- api
|--- root.rb
|--- helpers
|--- base_helper.rb
And inside root.rb, API is defined:
module API
class Root < Grape::API
formatter :json, Grape::Formatter::Jbuilder
helpers API::BaseHelper
end
end
Content of base_helper.rb is just simple:
module API
module BaseHelper
def test
"I am a test helper"
end
end
end
When I fire up the application, I get:
/Users/Larry/.rvm/gems/ruby-2.1.2#maleskine/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:481:in `load_missing_constant': Unable to autoload constant BaseHelper, expected /Users/Larry/Gallows/jianshu.io/maleskine/lib/api/helpers/base_helper.rb to define it (LoadError)
But base_helper.rb is actually in the right path mentioned in the error.
And if I remove helpers API::BaseHelper, autoload could find BaseHelper correctly.
Why is that? What have I done wrong?
After debugging into Rails source code, I found the problem.
First, since the helper file is inside helpers folder, the file should be defined inside Helpers module:
module API
module Helpers
module BaseHelper
def test
"I am a test helper"
end
end
end
end
Second, API::Helpers::BaseHelper has the path suffix api/helpers/base_helper, so make sure "#{Rails.root}/lib/" is inside your autoload path. ThenActiveSupport` will find it for you.
Make sure your BaseHelper is inside the API module. It should be defined like this:
module API
module BaseHelper
end
end
Try changing
helpers API::BaseHelper
to
helpers ::API::BaseHelper
to specify absolutely the class you're trying to load.
Related
I want to namespace all my classes put in a app/cms folder under Cms module. So, let's say i have a following file:
# /app/cms/types/post.rb
class Cms::Types::Post
end
Rails assumes that class definition of a file put in this dir should be Types::Post instead of Cms::Types::Post. So, when calling Cms::Types::Post.new, rails throws
LoadError (Unable to autoload constant Types::Post, expected /Users/xxx/workspace/personal/xxx/app/cms/types/post.rb to define it)
How can i namespace all these files under Cms?
I'm on rails 5.2.0
What is Post? I mean, what is the nature of Post? For instance, is it a service?
If it were a service (for example), I would put it in:
/app/services/cms/types/post_service.rb
And then define it as:
class Cms::Types::PostService
end
If it were a type (which seems like it might be a problematic name), then I would define it in:
/app/types/cms/post_type.rb
and define it as:
class Cms::PostType
end
In other words, name the directory under /app using the description of what Post is (just like rails does with models, controllers, etc.) and then add the description (in the singular form) to the end of your definition (rails does this with controllers, helpers, and mailers, but not with models).
Normally, when you use namespace you need to have your file structure as below:
module Cms
module Types
class Post
end
end
end
EDIT: Also to make it, autoload the path, you could add the below line in your application.rb:
config.autoload_paths << Rails.root.join('app', 'cms').to_s
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.
So the core issue at heart here is the following message:
`<class:ApplicationController>': uninitialized constant Xaaron::Core (NameError)
So I think some of the steps I have done will be redundant, but I am new to trying to add code to my library folder in a rails engine, in rails its rather easy. But here its not so much.
So here is what I have done:
in:
xaaron/
lib/
xaaron/
I have a directory called core/ with a file called loder.rb.
Inside of core is a directory called controllers and in side there is a file called user_controller which looks like:
module Xaaron
module Core
module UserController
def assign_to_member_group(user)
memeber = Xaron::Group.find('member')
user.add_group = memeber.group_name
end
end
end
end
To load this I have a loader file:
module Xaaron
module Core
module Loader
include Xaaron::Core::Controllers::UserController
end
end
end
Which I do not think is needed because in the engine.rb file I do: config.autoload_paths << File.expand_path("../xaaron/core/**", __FILE__) which just goes up one directory to the lib/ directory and loads xaaron/core/ and everything in it (or so I thought).
This loader.rb file is included in the ApplicationController
module Xaaron
class ApplicationController < ActionController::Base
...
include Xaaron::Core::Loader
...
end
end
So:
Whats the proper way to load my "core" library
Why am I getting the error above?
I guess your problem is with config.autoload_paths << File.expand_path("../xaaron/core/**", __FILE__). It expands into smth like Rails.root/lib/engine_name/xaaron/core/** and your lib path should be Rails.root/lib/xaaron/core. So, in your case, lib path should be config.autoload_paths << File.expand_path("../../xaaron/core", __FILE__)
Moreover, beeing within your ApplicationController, it is enough to include include Core::Loader, because you're already within Xaaron namespace.
Before you'll start working with controllers, try just call your module Xaaron::Core::Loader within rails console.
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'm trying to extract a portion of my Rails project into my lib directory but I can't work out how to link my files up correctly. My directory structure looks like this:
lib/
eventable/
calendar.rb
helpers.rb
# Rest of rails directories/files
I'm requiring the eventable directory in config/application.rb:
config.autoload_paths += %W(#{config.root}/lib #{config.root}/lib/eventable)
My helpers and calendar rb files:
# helpers.rb
module Eventable
module Helpers
def calendar_for...
Calendar.new...
end
end
end
# calendar.rb
module Eventable
class Calendar
# methods defined here
end
end
I'm then mixing my Eventable::Helpers module in the regular Rails helpers so that I can use calendar_for in my views:
ActionView::Base.send :include, Eventable::Helpers
This last step seems to work fine. However, when I go to a view which is using this helper I get:
uninitialized constant Eventable::Helpers::Calendar
If I change my helper so that it tries to access Eventable::Calendar.new instead then I get:
uninitialized constant Eventable::Calendar
When I had all of these in a single file, it all worked perfectly. So how I can correctly link these files up?
It looks like you need a loader-type file to tell Rails where to find code for the Eventable module.
Try add a lib/eventable.rb with:
module Eventable
autoload :Calendar, 'eventable/calendar'
autoload :Helpers, 'eventable/helpers'
end
You shouldn't need to change your load path if you have the loader file in place.
I had a similar problem. I solved it by changing the way modules are required.
In application.rb
config.autoload_paths += Dir["#{config.root}/lib/"]
Create /lib/eventable.rb with the following code
require "eventable/helpers"
require "eventable/calendar"