knowbody
knowbody

Reputation: 8276

Why does trying to use Grape with Rails fail with "uninitialized constant API"?

I would like someone to explain why this is happening in Rails (4.1.8) with Grape (0.10.1)

so this is my API:

app/api/root.rb:

module API
  class Root < Grape::API
    prefix 'api'
    mount API::V1::Root
  end
end

app/api/v1/root.rb:

module API::V1
  class Root < Grape::API
    version 'v1'
    mount API::V1::Users
  end
end

app/api/v1/users.rb:

module API::V1
  class Users < Grape::API
    format 'json'

    resource :users do
      desc "Return list of users"
      get '/' do
        User.all
      end
    end
  end
end

config/routes.rb:

Rails.application.routes.draw do
  mount API::Root => '/'
end

and in my application.rb I added:

config.paths.add "app/api", glob: "**/*.rb"
config.autoload_paths += Dir["#{Rails.root}/app/api/*"]

and in that case I get the error: NameError: uninitialized constant API


but if my code looks like:

app/api/root.rb same as above

then app/api/v1/root.rb:

class Root < Grape::API
  version 'v1'
  mount Users
end

app/api/v1/users.rb:

class Users < Grape::API
  format 'json'

  resource :users do
    desc "Return list of users"
    get '/' do
      User.all
    end
  end
end

config/routes.rb:

Rails.application.routes.draw do
  mount Root => '/'
end

and config/application.rb same as above

Then everything works fine.

My question is why don't I need to specify modules inside v1/root.rb and also inside v1/users and also why I don't need to use API::Root => '/' in config/routes.rb?

Upvotes: 2

Views: 4548

Answers (3)

Roshan
Roshan

Reputation: 915

From Grape's documentation:

For Rails versions greater than 6.0.0.beta2, Zeitwerk autoloader is the default for CRuby. By default Zeitwerk inflects api as Api instead of API. To make our example work, you need to uncomment the lines at the bottom of config/initializers/inflections.rb, and add API as an acronym:

ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.acronym 'API'
end

Upvotes: 6

Fei
Fei

Reputation: 1207

I tried to put my files into app/api/api, but it wasn't working for me.

I found a solution by simply putting the api folder into the controller folder. I'm not 100% sure what the problem was, but my guess is that it has something to do with the auto-loaded paths.

Upvotes: 1

user473305
user473305

Reputation:

It's because app/api is the top-level folder for your API classes, not app.

From Grape's documentation:

Place API files into app/api. Rails expects a subdirectory that matches the name of the Ruby module and a file name that matches the name of the class. In our example, the file name location and directory for Twitter::API should be app/api/twitter/api.rb.

Therefore the correct location for an API::Root class would actually be app/api/api/root.rb, not /app/api/root.rb—though that is the correct location for a class in the top-level namespace, which is why the second example you give (with classes removed from the API module) works.

I recommend you keep your API classes together in their own module, though, and move them to a matching subfolder beneath app/api.

Upvotes: 8

Related Questions