Ruby on Rails。未初始化的常量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21041653/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 23:12:41  来源:igfitidea点击:

Ruby on Rails. Uninitialized constant

ruby-on-railsrubycarrierwave

提问by Nemoden

This is odd, but:

这很奇怪,但是:

Uploader class (app/uploaders):

上传器类(应用程序/上传器):

class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick

  # ....

  version :thumb, from_version: :preview do
    process resize_to_limit: [Image::THUMB_WIDTH]
  end

Image class (app/models):

图像类(应用程序/模型):

class Image < ActiveRecord::Base
  include Rails.application.routes.url_helpers
  mount_uploader :image, ImageUploader

  THUMB_WIDTH = 220
  PREVIEW_WIDTH = 460
  MAX_WIDTH = 960

The application says:

该应用程序说:

uninitialized constant Image::THUMB_WIDTH

uninitialized constant Image::THUMB_WIDTH

  version :thumb, from_version: :preview do
    process resize_to_limit: [Image::THUMB_WIDTH] #<<<----
  end

  version :preview, from_version: :fullsize do

What's wrong?

怎么了?

UPDATE:

更新:

Agis pointed out the reason.

阿吉斯指出了原因。

Bounty for the best solution to this problem will be applied in 2 days. I don't like code separation, e.g. making a new class holding all the constants for the Image class in initializers etc. This solutions is bad because it it brings inconsistency and code fragmentation.

将在 2 天内申请此问题的最佳解决方案的赏金。我不喜欢代码分离,例如在初始化器中创建一个包含 Image 类的所有常量的新类等。这种解决方案很糟糕,因为它带来了不一致和代码碎片。

回答by bbozo

A general answer

一般答案

You have a chicken or egg case as far as rails autoloader is concerned, the "rails way" of solving this in a general sense would be to refactor the metacode so that the class names get passed as string values rather than classes, for example:

就 Rails 自动加载器而言,您有一个鸡或蛋的情况,一般意义上解决这个问题的“rails 方法”是重构元代码,以便类名作为字符串值而不是类传递,例如:

belongs_to :manager, class_name: "Employee"

belongs_towill call constantizeon class_namehopefully at a time when all classes have been loaded so the chicken and egg issue is circumvented "the rails way".

belongs_to在所有课程都已加载的时候希望会调用constantizeclass_name这样鸡和蛋的问题就可以“轨道方式”规避了。

What @Stoicsuggested is essentially a variation of this theme of circumventing the evaluation of image.rbat image_uploader.rbload time:

什么@Stoic建议实质上是回避的评价这一主题的变化image.rb,在image_uploader.rb加载时间:

model.class.const_get("THUMB_WIDTH")

could also have been phrased as:

也可以表述为:

'Image'.constantize.const_get("THUMB_WIDTH")

and result is the same and the general lesson to take from this is: avoid using another class name literals in class load-time codeor in other words belongs_to :manager, class_name: "Employee"is good and belongs_to :manager, class_name: Employeewould be bad.

结果是一样的,从中得到的一般教训是:避免在类加载时代码中使用另一个类名文字,或者换句话说belongs_to :manager, class_name: "Employee",好而belongs_to :manager, class_name: Employee坏。

It's not pretty, but its probably the most elegant universal way to avoid these headaches

它不漂亮,但它可能是避免这些头痛的最优雅的通​​用方法



A problem specific answer

特定问题的答案

A different way of looking at the problem is that thumbnail icon width is actually a concern of the uploaderand not of the model and that you're in fact seeing a fringe case of inappropriate intimacy code smell (http://www.codinghorror.com/blog/2006/05/code-smells.html).

看待问题的另一种方式是缩略图图标宽度实际上是上传者而不是模型的问题,并且您实际上看到的是不适当的亲密代码气味的边缘案例(http://www.codinghorror.jpg)。 com/blog/2006/05/code-smells.html)。

Watch out for classes that spend too much time together, or classes that interface in inappropriate ways. Classes should know as little as possible about each other.

注意那些花太多时间在一起的类,或者以不适当的方式接口的类。班级之间应该尽可能少地了解彼此。

So, if you are of this school of thought (I'm leaning in this direction) the solution would be to make THUMB_WIDTHa constant of the ImageUploaderclass and the problem goes away.

因此,如果您属于这种思想流派(我倾向于这个方向),那么解决方案是使类THUMB_WIDTH保持不变,ImageUploader问题就会消失。

It's generally a good idea to separate different domain concerns out of the models anyway as the models like to get bloated and unmanageable - you could view the uploader class as a service class of your model designed to extract a particular domain problem much in the same way value objects, form objects etc get handled in http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

无论如何,将不同的领域问题从模型中分离出来通常是个好主意,因为模型喜欢变得臃肿且难以管理 - 您可以将上传器类视为模型的服务类,旨在以相同的方式提取特定的领域问题值对象、表单对象等在http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ 中处理



Plan C would be to switch places of config.autoload_pathsin your application.rband cross fingers :)

计划 C 将交换config.autoload_pathsapplication.rb和交叉手指的位置:)

回答by Agis

This happens because the code for the ImageUploaderclass is evaluated beforethe one for the Imageclass. So at the point where Image::THUMB_WIDTHis evaluated, the constant lookup happens and searches for the Imageconstant but does not find it since the relevant file is not loaded yet.

这是因为在代码ImageUploader级评估之前,一只为Image类。因此,在Image::THUMB_WIDTH评估时,常量查找发生并搜索Image常量但未找到它,因为相关文件尚未加载。

You can fix this by adding this definition at the top of your ImageUploaderfile:

您可以通过在ImageUploader文件顶部添加此定义来解决此问题:

class Image; end

You could also do the same but in an initializer instead (ex. config/initializers/image.rb).

你也可以做同样的事情,但在初始化程序中代替(例如config/initializers/image.rb)。

This would make sure that the Imageclass is loaded at the beginning of the boot process, before everything else and may be considered a clearer solution to you in case you don't want to have definitions of different classes in the same file.

这将确保Image在启动过程开始时加载该类,在其他一切之前,如果您不想在同一文件中定义不同的类,则可能被认为是一个更清晰的解决方案。

回答by S.Spencer

I think you should probably move the constants out of the class, and into the wider namespace in an initializer. Or you could create a method on the image class to access the constant instead. This may or may not alleviate your problems.

我认为您应该将常量移出类,并移入初始化程序中更广泛的命名空间。或者您可以在图像类上创建一个方法来访问常量。这可能会也可能不会缓解您的问题。

class Image
  THUMB_WIDTH = 220

  def self.thumb_width
    THUMB_WIDTH
  end
end

process resize_to_limit: [Image.thumb_width]

回答by Nick Urban

Rails provides a convenient place to store configuration data: Rails.application.config. This will solve your dependency problem and nicely separate the configuration from the logic. I know you said you prefer not to separate them, but I think this is pretty clean (better than creating a constants module).

Rails 提供了一个方便的地方来存储配置数据:Rails.application.config. 这将解决您的依赖问题,并将配置与逻辑很好地分开。我知道你说过你不想把它们分开,但我认为这很干净(比创建一个常量模块更好)。

In config/application.rb:

在 config/application.rb 中:

config.image_sizes = {
  thumb_width: 220,
  preview_width: 460,
  max_width: 960,
}.with_indifferent_access

In app/uploaders/image_uploader.rb:

在 app/uploaders/image_uploader.rb 中:

version :thumb, from_version: :preview do
  process resize_to_limit: [Rails.application.config.image_sizes[:thumb_width]]
end

回答by davogones

Your ImageUploaderclass is being autoloaded before THUMB_WIDTHhas been defined. Change the definition order:

您的ImageUploaderTHUMB_WIDTH在定义之前正在自动加载。更改定义顺序:

class Image < ActiveRecord::Base
  include Rails.application.routes.url_helpers

  THUMB_WIDTH = 220
  PREVIEW_WIDTH = 460
  MAX_WIDTH = 960

  mount_uploader :image, ImageUploader

end

回答by fny

Keeping in mind the fact that evaluation order is very important in dynamic languages, it's best to always structure your code in a fashion that will protect you against issues like this. Consider structuring your code in the following fashion:

请记住,评估顺序在动态语言中非常重要,最好始终以一种可以保护您免受此类问题的方式来构建代码。考虑以下列方式构建您的代码:

class Example
  extend Extensions
  include Inclusions
  CONSTANTS = "Should Consistently Follow"

  attr_accessor :notebook
  attr_reader :book
  attr_writer :note

  then_other_available :macro_like_methods

  def initialize; end

  def self.class_methods; end

  def instance_methods; end

  protected
    # ...
  private
    # ...
end

回答by nathanvda

Adding require Imageat the top of ImageUploaderdoes not help?

require Image在顶部添加ImageUploader没有帮助?

Not sure, because Imageneeds ImageUploader, so you might have a circular reference there.

不确定,因为Image需要ImageUploader,所以你可能在那里有一个循环引用。

So if that does not work, I would extract the image-configuration to another class/module. You say it belongs to the Imagebut imho it is more application-configuration, right? It specifies how images are shown/stored in this application. If you would show images in another application, it might change.

因此,如果这不起作用,我会将图像配置提取到另一个类/模块。你说它属于Image应用程序,但恕我直言,它更像是应用程序配置,对吗?它指定如何在此应用程序中显示/存储图像。如果您要在另一个应用程序中显示图像,它可能会改变。

So, either you add something like app/models/image/configuration.rb

所以,要么你添加类似的东西 app/models/image/configuration.rb

class Image
  module Configuration
    THUMB_WIDTH = 220
    PREVIEW_WIDTH = 460
    MAX_WIDTH = 960
  end
end

and in your ImageUploaderyou can then write

然后在你的ImageUploader你可以写

require `image/configuration`

and use Image::Configuration::THUMB_WIDTH.

并使用Image::Configuration::THUMB_WIDTH.

What I generally do with such configuration properties: I put them in a config.yml, which I load in an initializer. It is really easy do to yourself, using something like

我通常使用此类配置属性做什么:我将它们放在 a 中config.yml,然后将其加载到初始化程序中。对自己做真的很容易,使用类似的东西

appl_config_file = "#{Rails.root}/config/config.yml"
raw_config = File.read(appl_config_file)
APP_CONFIG = HashWithIndifferentAccess.new(YAML.load(raw_config)["#{Rails.env}"])

or use a gem like rails_config.

或者使用像rails_config这样的gem

回答by Meredian

I can't be certain about problem roots, but can assume it is related to file loading order. Content of classscope evaluated at the moment of loading file. And name Image::THUMB_WIDTHused before it is ever defined (and file loaded).

我不能确定问题根源,但可以假设它与文件加载顺序有关。class加载文件时评估的范围内容。以及Image::THUMB_WIDTH在定义(和加载文件)之前使用的名称。

But still not sure, because received message is not uninitialized constant Image, but uninitialized constant Image::THUMB_WIDTH. It can related to some project details and structure which is not described in current question context.

但仍然不确定,因为收到的消息不是uninitialized constant Image,而是uninitialized constant Image::THUMB_WIDTH。它可能与当前问题上下文中未描述的一些项目细节和结构有关。

Solution using const_getcan be named hack (dirty, nasty hack), since you are not sure if it is ever initialized at the loading moment in current situation.

使用的解决方案const_get可以命名为 hack (dirty, nasty hack),因为您不确定在当前情况下它是否在加载时刻被初始化。

I would say this code has design issues, cause classes share responsibility and require each other at the moment of code evaluation. Maybe you should move this dependency to initialization time, for example - passing expected images parameters to ImageUploaderwhile calling new? Or better to move it completely there from Image, since autogenerated thumb size is barely part of Imageabstraction. It depends on Imageclass purpose, you should clarify it first.

我会说这段代码存在设计问题,导致类在代码评估时分担责任并相互要求。也许您应该将此依赖项移至初始化时间,例如 -ImageUploader在调用 new 时将预期的图像参数传递给?或者更好地将它从 完全移到那里Image,因为自动生成的拇指大小几乎不是Image抽象的一部分。这取决于Image课程目的,你应该先澄清它。

Remember, single object - single responsibility. This way will solve dependency problem in most general and controllable way.

请记住,单一对象 - 单一职责。这种方式将以最通用和可控的方式解决依赖问题。

回答by Stoic

Looking at the source for this RailsCasts Episode, I think you should use:

查看此 RailsCasts Episode的来源,我认为您应该使用:

process resize_to_limit: [model.class.const_get("THUMB_WIDTH")]

The benefit with this method is that you can simply declare a THUMB_WIDTHconstant inside any other model, and then simply, use this uploader class to handle that model, as well :)

这种方法的好处是你可以简单THUMB_WIDTH地在任何其他模型中声明一个常量,然后简单地使用这个上传器类来处理该模型,以及:)