web-dev-qa-db-fra.com

Rails: façon élégante de structurer les modèles en sous-dossiers sans créer de sous-modules

J'ai de nombreux modèles dans mon dossier app/models. Je voudrais nettoyer un peu ce dossier. Déplacez les modèles qui appartiennent les uns aux autres dans des sous-dossiers. Le problème est que, par convention, la classe de modèle est espacée dans un module correspondant.

Par exemple.

app/models/blog/post.rb
app/models/blog/comment.rb
app/models/user.rb

pour que:

app/models/blog/post.rb

class Post < ActiveRecord
end

et pas

class Blog::Post < ActiveRecord
end
61
seb

Voici ce que j'ai utilisé pour Rails 3:

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

Cette configuration indique Rails pour analyser tous les sous-dossiers app/models récursivement et charger tous les modèles trouvés. Aucun espace de noms requis.

82
Ion Br.

Nous devions le faire, et il existe un moyen très simple.

déplacez vos modèles dans les sous-dossiers, puis dites à Rails de charger les fichiers de tous les sous-dossiers de votre fichier environment.rb:

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

Aucun espace de noms requis, et les modèles peuvent être considérés comme normaux dans votre application

12
Tilendor

J'ai également créé des sous-dossiers, puis ajouté les éléments suivants au fichier application.rb:

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

Mais cela ne suffit pas lorsque les sous-dossiers sont nommés en utilisant le même nom qu'un modèle (par exemple, un dossier `` utilisateur '' contenant plusieurs fichiers, dont l'un est `` utilisateur ''). Cela provoquait toutes sortes d'erreurs dans mon code jusqu'à ce que je trouve qu'il pourrait être résolu en donnant simplement aux dossiers des noms différents des modèles (par exemple, `` modèles utilisateur '') qu'ils contiennent. J'ai trouvé la suggestion à http://www.williambharding.com/blog/technology/Rails-3-autoload-modules-and-classes-in-production/ , ce qui pointe en fait vers cette question.

4
pickwick

Donc j'avais l'habitude d'avoir en Rails 2 quelque chose comme ça:

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

Et les fichiers suivants:

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

Lorsque je suis passé à Rails 3, j'ai continué à recevoir une erreur dans le sens suivant: Expected .../app/models/user/foo.rb to define Foo. Cela semblait clairement fou puisque Rails 2 supposait automatiquement que ce que vous mettriez dans user/foo.rb serait User::Foo pas seulement Foo.

Donc, la façon dont j'ai fini par résoudre cela était de se débarrasser des sous-répertoires de modèles dans autoload_paths et faire quelque chose comme ça:

J'ai créé app/models/user.rb avec:

module User
  autoload :User, 'user/base'
  autoload :User, 'user/admin'
end
3
Aaron Gibralter

Dans mon Rails 3.2.3 app, après avoir déplacé certains modèles dans des sous-répertoires, je suis tombé sur des erreurs comme

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

pour les appels d'association (exemple: Project.first.project_category).

En fin de compte, la solution de contournement que j'ai trouvée consistait à définir: nom_classe pour chaque association à modéliser dans le sous-répertoire ...

class Project < ActiveRecord::Base

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

end

La partie "::" indique ici Rails que le modèle ProjectCategory n'a pas d'espace de nom, malgré le fait qu'il soit défini dans un sous-répertoire 'models/project'.

1
Tim Zaripov

Vous pourriez peut-être regarder RailsEngines. Ce n'est pas exactement ce dont vous avez besoin, mais pourrait vous donner quelques idées.

En dehors de cela, si votre script semble fonctionner correctement (vous pouvez également lire tous les fichiers de chaque sous-dossier sur le modèle et les exiger), je ne vois aucun problème contre cela.

1
Yaraher

cette version de la solution de Tilendor fonctionne avec Rails 3

config.load_paths et Rails_ROOT sont dépréciés dans Rails 3, vous devez également les mettre dans le bloc de configuration de config/application.rb, et non environment.rb

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

Cela a fonctionné pour moi dans Rails 5.

Ajout de ce qui suit à application.rb

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

Attention cependant, vous ne pouvez pas avoir le même nom sur votre dossier que n'importe lequel de vos modèles.

1
Fellow Stranger

Toutes les réponses ci-dessus n'ont pas fonctionné pour moi. D'une manière ou d'une autre, le dossier "models" était chargé de sous-dossiers, ce qui entraînait "Expected to contain ::.

La plupart de mes sous-répertoires étaient des classes STI, je les ai donc déplacées vers app/models_sti // *. Ensuite, tout ce que je devais faire était de mettre 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
0
Kangur

Jusqu'à ce que je trouve une meilleure solution, j'ai créé un init.rb dans le dossier app/models:

app/models/init.rb

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

Servit le but jusqu'à présent.

0
seb