[英]Ruby on Rails. Engine. Uninitialized constant error on production
[英]Ruby on Rails. Uninitialized constant
這很奇怪,但是:
上傳器類(app / uploaders):
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
# ....
version :thumb, from_version: :preview do
process resize_to_limit: [Image::THUMB_WIDTH]
end
圖像類(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
申請說:
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
怎么了?
更新:
阿吉斯指出了原因。
Bounty將在2天內應用此問題的最佳解決方案。 我不喜歡代碼分離,例如在初始化器中創建一個新類,用於保存Image類的所有常量。這個解決方案很糟糕,因為它帶來了不一致和代碼碎片。
就rails自動加載器而言,你有一個雞或蛋的情況,在一般意義上解決這個問題的“軌道方式”是重構元代碼,以便類名作為字符串值而不是類傳遞,例如:
belongs_to :manager, class_name: "Employee"
在所有類都被加載的時候, belongs_to
會在class_name
上調用constantize
,這樣就可以避免雞和蛋的問題“軌道”。
@Stoic建議的內容本質上是繞過image.rb
在image_uploader.rb
加載時的評估主題的變體:
model.class.const_get("THUMB_WIDTH")
也可以被表述為:
'Image'.constantize.const_get("THUMB_WIDTH")
結果是一樣的,從中可以得出的一般教訓是: 避免在類加載時代碼中使用另一個類名文字,或者換句話說belongs_to :manager, class_name: "Employee"
是好的, belongs_to :manager, class_name: Employee
會很糟糕的。
它不漂亮,但它可能是避免這些頭痛的最優雅的通用方法
查看問題的一種不同方式是縮略圖圖標寬度實際上是上傳者而不是模型的關注點,並且您實際上看到了一個不恰當的親密代碼氣味的邊緣情況( http://www.codinghorror。 com / blog / 2006/05 / code-smells.html )。
注意那些花費太多時間在一起的課程,或者以不恰當的方式進行交流的課程。 課程應該盡可能少地了解彼此。
所以,如果你是這個思想學派(我傾向於這個方向),解決方案是使THUMB_WIDTH
成為ImageUploader
類的常量,問題就會消失。
無論如何,將模型中的不同域關注點分離出來通常是一個好主意,因為模型會變得臃腫和無法管理 - 您可以將上傳器類視為模型的服務類,旨在以相同的方式提取特定的域問題值對象,表單對象等在http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/中處理
計划C將在你的application.rb
和交叉手指切換config.autoload_paths
位置:)
發生這種情況是因為ImageUploader
類的代碼在Image
類的代碼之前進行了評估。 因此,在評估Image::THUMB_WIDTH
,發生常量查找並搜索Image
常量但由於尚未加載相關文件而找不到它。
您可以通過在ImageUploader
文件的頂部添加此定義來解決此問題:
class Image; end
您也可以在初始化程序中執行相同操作(例如config/initializers/image.rb
)。
這將確保Image
類在引導過程開始時加載,在其他所有內容之前加載,如果您不希望在同一文件中定義不同類,則可以將其視為更清晰的解決方案。
我認為您應該將常量移出類,並在初始化程序中移動到更寬的命名空間中。 或者您可以在圖像類上創建一個方法來訪問常量。 這可能會也可能不會緩解您的問題。
class Image
THUMB_WIDTH = 220
def self.thumb_width
THUMB_WIDTH
end
end
process resize_to_limit: [Image.thumb_width]
Rails提供了存儲配置數據的便利位置: Rails.application.config
。 這將解決您的依賴性問題,並很好地將配置與邏輯分開。 我知道你說你不想把它們分開,但我覺得這很干凈(比創建一個常量模塊更好)。
在config / application.rb中:
config.image_sizes = {
thumb_width: 220,
preview_width: 460,
max_width: 960,
}.with_indifferent_access
在app / uploaders / image_uploader.rb中:
version :thumb, from_version: :preview do
process resize_to_limit: [Rails.application.config.image_sizes[:thumb_width]]
end
在定義THUMB_WIDTH
之前,您的ImageUploader
類正在自動加載。 更改定義順序:
class Image < ActiveRecord::Base
include Rails.application.routes.url_helpers
THUMB_WIDTH = 220
PREVIEW_WIDTH = 460
MAX_WIDTH = 960
mount_uploader :image, ImageUploader
end
我不能確定問題根,但可以假設它與文件加載順序有關。 加載文件時評估的class
范圍內容。 並命名Image::THUMB_WIDTH
在它被定義(和文件加載)之前使用。
但仍不確定,因為收到的消息不是uninitialized constant Image
,而是uninitialized constant Image::THUMB_WIDTH
。 它可以涉及一些項目細節和結構,這在當前問題上下文中沒有描述。
使用const_get
解決方案可以命名為hack(dirty,nasty hack),因為你不確定它是否在當前情況下的加載時刻進行了初始化。
我會說這段代碼有設計問題,導致類共享責任,並且在代碼評估時需要彼此。 也許您應該將此依賴關系移動到初始化時間,例如 - 在調用new時將預期的圖像參數傳遞給ImageUploader
? 或者更好地從Image
完全移動它,因為自動生成的拇指大小幾乎不是Image
抽象的一部分。 它取決於Image
類的目的,你應該首先澄清它。
記住,單一對象 - 單一責任。 這種方式將以最通用和可控的方式解決依賴問題。
在ImageUploader
頂部添加require Image
無濟於事?
不確定,因為Image
需要ImageUploader
,所以你可能在那里有一個循環引用。
因此,如果這不起作用,我會將圖像配置提取到另一個類/模塊。 你說它屬於Image
但是imho它更多是應用程序配置,對吧? 它指定了如何在此應用程序中顯示/存儲圖像。 如果要在其他應用程序中顯示圖像,則可能會更改。
所以,要么添加app/models/image/configuration.rb
class Image
module Configuration
THUMB_WIDTH = 220
PREVIEW_WIDTH = 460
MAX_WIDTH = 960
end
end
然后在你的ImageUploader
你可以寫
require `image/configuration`
並使用Image::Configuration::THUMB_WIDTH
。
我通常使用這樣的配置屬性:我將它們放在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}"])
或使用像rails_config這樣的gem 。
請記住評估順序在動態語言中非常重要這一事實,最好始終以一種方式構建代碼,以保護您免受此類問題的影響。 考慮以下列方式構建代碼:
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
看看這個RailsCasts劇集的來源,我認為你應該使用:
process resize_to_limit: [model.class.const_get("THUMB_WIDTH")]
這種方法的好處是你可以在任何其他模型中簡單地聲明一個THUMB_WIDTH
常量,然后簡單地使用這個上傳器類來處理該模型,以及:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.