为什么尝试将 Grape 与 Rails 一起使用会因 "uninitialized constant API" 而失败?
Why does trying to use Grape with Rails fail with "uninitialized constant API"?
我希望有人能解释为什么 Rails (4.1.8) 和 Grape (0.10.1)
会发生这种情况
所以这是我的 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
并在我的 application.rb
中添加:
config.paths.add "app/api", glob: "**/*.rb"
config.autoload_paths += Dir["#{Rails.root}/app/api/*"]
在那种情况下我得到错误:NameError: uninitialized constant API
但如果我的代码如下所示:
app/api/root.rb
同上
然后 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
和config/application.rb
同上
然后一切正常。
我的问题是为什么我不需要在 v1/root.rb
和 v1/users
中指定模块以及为什么我不需要在 [=23] 中使用 API::Root => '/'
=]?
这是因为 app/api
是您 API class 的顶级文件夹,而不是 app
。
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
.
因此 API::Root
class 的正确位置实际上是 app/api/api/root.rb
,而不是 /app/api/root.rb
——尽管 是 class 在顶级命名空间中的正确位置,这就是为什么你给出的第二个例子(classes 从 API
模块中删除)有效。
不过,我建议您将 API class 放在各自的模块中,然后将它们移动到 app/api
.
下的匹配子文件夹中
我试图将我的文件放入 app/api/api
,但它对我不起作用。
我找到了一个解决方案,只需将 api
文件夹放入 controller
文件夹即可。我不是 100% 确定问题出在哪里,但我的猜测是它与自动加载的路径有关。
对于大于 6.0.0.beta2 的 Rails 版本,Zeitwerk
自动加载程序是 CRuby 的默认设置。默认情况下 Zeitwerk
将 api
变形为 Api
而不是 API
。为了使我们的示例工作,您需要取消注释 config/initializers/inflections.rb
底部的行,并添加 API
作为首字母缩写词:
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym 'API'
end
我希望有人能解释为什么 Rails (4.1.8) 和 Grape (0.10.1)
会发生这种情况所以这是我的 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
并在我的 application.rb
中添加:
config.paths.add "app/api", glob: "**/*.rb"
config.autoload_paths += Dir["#{Rails.root}/app/api/*"]
在那种情况下我得到错误:NameError: uninitialized constant API
但如果我的代码如下所示:
app/api/root.rb
同上
然后 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
和config/application.rb
同上
然后一切正常。
我的问题是为什么我不需要在 v1/root.rb
和 v1/users
中指定模块以及为什么我不需要在 [=23] 中使用 API::Root => '/'
=]?
这是因为 app/api
是您 API class 的顶级文件夹,而不是 app
。
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 forTwitter::API
should beapp/api/twitter/api.rb
.
因此 API::Root
class 的正确位置实际上是 app/api/api/root.rb
,而不是 /app/api/root.rb
——尽管 是 class 在顶级命名空间中的正确位置,这就是为什么你给出的第二个例子(classes 从 API
模块中删除)有效。
不过,我建议您将 API class 放在各自的模块中,然后将它们移动到 app/api
.
我试图将我的文件放入 app/api/api
,但它对我不起作用。
我找到了一个解决方案,只需将 api
文件夹放入 controller
文件夹即可。我不是 100% 确定问题出在哪里,但我的猜测是它与自动加载的路径有关。
对于大于 6.0.0.beta2 的 Rails 版本,Zeitwerk
自动加载程序是 CRuby 的默认设置。默认情况下 Zeitwerk
将 api
变形为 Api
而不是 API
。为了使我们的示例工作,您需要取消注释 config/initializers/inflections.rb
底部的行,并添加 API
作为首字母缩写词:
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym 'API'
end