简体   繁体   English

Rails:将模型结构化为子文件夹而不创建子模块的优雅方式

[英]Rails: Elegant way to structure models into subfolders without creating submodules

I have numerous models in my app/models folder. 我的app / models文件夹中有很多模型。 I'd like to clean this folder up a little bit. 我想稍微清理一下这个文件夹。 Move models that belong to each other in subfolders. 在子文件夹中移动彼此属于的模型。 The problem is that by convention the model class is namespaced into an according module. 问题是按照惯例,模型类被命名为相应的模块。

Eg 例如

app/models/blog/post.rb 应用程序/模型/博客/ post.rb
app/models/blog/comment.rb 应用程序/模型/博客/ comment.rb
app/models/user.rb 应用程序/模型/ user.rb

so that: 以便:

app/models/blog/post.rb 应用程序/模型/博客/ post.rb

class Post < ActiveRecord
end

and not 并不是

class Blog::Post < ActiveRecord
end

Here is what I used for Rails 3: 这是我用于Rails 3的内容:

config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**}')]

This configuration tells Rails to scan all the app/models subfolders recursively and load all found models. 此配置告诉Rails递归扫描所有app / models子文件夹并加载所有找到的模型。 No namespacing required. 不需要命名空间。

We needed to do this, and there is a very simple way. 我们需要这样做,而且有一种非常简单的方法。

move your models into the sub-folders, and then tell rails to load files from all subfolders in your environment.rb file: 将模型移动到子文件夹中,然后告诉rails从environment.rb文件中的所有子文件夹加载文件:

config.load_paths += Dir["#{RAILS_ROOT}/app/models/*"].find_all { |f| File.stat(f).directory? }

No namespacing required, and the models can be referred to as normal in your app 不需要命名空间,并且可以在您的应用中将模型称为正常模型

I also created subfolders, and then added the following to the application.rb file: 我还创建了子文件夹,然后将以下内容添加到application.rb文件中:

config.autoload_paths += Dir["#{config.root}/app/models/**/"]

But doing this alone isn't enough when subfolders are named using the same name as a model (eg, a folder 'user' containing several files, one of which is 'user'). 但是当使用与模型相同的名称命名子文件夹时(例如,包含多个文件的文件夹'user',其中一个是'user'),单独执行此操作是不够的。 This was causing all kinds of errors in my code until I found that it could be solved by simply giving the folders names that are different from the models (eg, 'user models') they contain. 这导致我的代码中出现各种错误,直到我发现它可以通过简单地给出与它们包含的模型(例如,“用户模型”)不同的文件夹名称来解决。 I found the suggestion at http://www.williambharding.com/blog/technology/rails-3-autoload-modules-and-classes-in-production/ , which actually points to this question. 我在http://www.williambharding.com/blog/technology/rails-3-autoload-modules-and-classes-in-production/找到了这个建议,这实际上指的是这个问题。

So I used to have in Rails 2 something like this: 所以我以前在Rails 2中有这样的东西:

config.autoload_paths += Dir["#{config.root}/app/models/**/"]

And the following files: 以下文件:

  • app/models/user/base.rb: class User::Base app / models / user / base.rb: class User::Base
  • app/models/user/admin.rb: class User::Admin app / models / user / admin.rb: class User::Admin

When I upgraded to Rails 3, I kept getting an error along these lines: Expected .../app/models/user/foo.rb to define Foo . 当我升级到Rails 3时,我不断遇到错误: Expected .../app/models/user/foo.rb to define Foo This clearly seemed crazy since Rails 2 automatically assumed that what you put in user/foo.rb would be User::Foo not just Foo . 这显然看起来很疯狂,因为Rails 2自动假设你在user / foo.rb中输入的内容是User::Foo而不仅仅是Foo

So the way I ended up solving this was getting rid of model subdirectories in autoload_paths and doing something like this: 所以我最终解决这个问题的方法是在autoload_paths删除模型子目录并执行以下操作:

I created app/models/user.rb with: 我创建了app / models / user.rb:

module User
  autoload :User, 'user/base'
  autoload :User, 'user/admin'
end

In my Rails 3.2.3 app, after i moved some models to subdirectories, i have stumbled into errors like 在我的Rails 3.2.3应用程序中,在我将一些模型移动到子目录之后,我偶然发现了错误

Expected /.../.../app/models/project/project_category.rb to define Project::ProjectCategory

for association calls (example: Project.first.project_category). 用于关联调用(例如:Project.first.project_category)。

In the end, the workaround i found was to set :class_name for every association to model in subdirectory... 最后,我找到的解决方法是为每个在子目录中建模的关联设置:class_name ...

class Project < ActiveRecord::Base

  belongs_to :project_category, :class_name => "::ProjectCategory"

end

"::" part here points to Rails that ProjectCategory model has no namespace, despite the fact that is defined in a 'models/project' subdirectory. “::”部分指向Rails,ProjectCategory模型没有命名空间,尽管事实是在'models / project'子目录中定义的。

Maybe you could look upon RailsEngines. 也许你可以看看RailsEngines。 It's not exactly what you need, but could gave you some ideas. 这不完全是你需要的,但可以给你一些想法。

Other than that, if your script seems to work fine (you could also just read all the files on each subfolder on model and require them), I don't see any problem against it. 除此之外,如果您的脚本似乎工作正常(您也可以只读取模型上每个子文件夹上的所有文件并要求它们),我没有看到任何问题。

This worked for me in Rails 5. 这在Rails 5中对我有用。

Adding the following to application.rb 将以下内容添加到application.rb

config.autoload_paths += Dir[ Rails.root.join('app/models/**/') ]

Beware though that you cannot have the same name on your folder as any of your models. 请注意,您的文件夹上的名称不能与任何型号相同。

this version of Tilendor's solution works with Rails 3 这个版本的Tilendor解决方案适用于Rails 3

config.load_paths and RAILS_ROOT are deprecated in Rails 3, also you should put it in the config block of the config/application.rb, not environment.rb 在Rails 3中不推荐使用config.load_paths和RAILS_ROOT,也应该将它放在config / application.rb的config块中,而不是environment.rb

config.autoload_paths += Dir["#{Rails.root.to_s}/app/models/*"].find_all { |f| File.stat(f).directory? }

Until I find a better solution I've created a init.rb in the app/models folder: 在找到更好的解决方案之前,我在app / models文件夹中创建了一个init.rb:

app/models/init.rb 应用程序/模型/ init.rb

%w[blog].each do |folder|
  path = [File.dirname(__FILE__), folder, "*.rb"].join('/')
  Dir[path].each {|file| require file }  
end

Servers the purpose until now. 直到现在服务器的目的。

All the above answers didn't work for me. 以上所有答案对我都不起作用。 Somehow 'models' folder was loaded with subfolders, which resulted in 'Expected to contain ::. 不知何故'models'文件夹加载了子文件夹,导致'期望包含::。

Most of my subdirs were STI classes so I've moved them to app/models_sti//*. 我的大多数子目录都是STI类,所以我把它们移到了app / models_sti // *。 Then all I needed to do was to put in application.rb 然后我需要做的就是放入application.rb

#config.autoload_paths += Dir["#{Rails.root.to_s}/app/models/**/"]
# Loaded dynamically (cache_classes == false ?)
config.autoload_paths << Rails.root.join('app', 'models').to_s
config.autoload_paths += Dir["#{Rails.root.to_s}/app/models_sti/*"].find_all { |f| File.stat(f).directory? }

# Eager_load_paths - used only when cache_classes == true [rails spec says so]
config.eager_load_paths.delete_if do |path|
  # If I didn't delete it from eager_load_paths I got errors even in develop
  path == Rails.root.join('app', 'models_sti').to_s
end
config.eager_load_paths += config.autoload_paths

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM