Ruby on Rails. Uninitialized constant
Solution 1
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:
belongs_to :manager, class_name: "Employee"
belongs_to
will call constantize
on class_name
hopefully at a time when all classes have been loaded so the chicken and egg issue is circumvented "the rails way".
What @Stoic suggested is essentially a variation of this theme of circumventing the evaluation of image.rb
at image_uploader.rb
load time:
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 code or in other words belongs_to :manager, class_name: "Employee"
is good and belongs_to :manager, class_name: Employee
would be bad.
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 uploader and 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).
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_WIDTH
a constant of the ImageUploader
class and the problem goes away.
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/
Plan C would be to switch places of config.autoload_paths
in your application.rb
and cross fingers :)
Solution 2
This happens because the code for the ImageUploader
class is evaluated before the one for the Image
class. So at the point where Image::THUMB_WIDTH
is evaluated, the constant lookup happens and searches for the Image
constant but does not find it since the relevant file is not loaded yet.
You can fix this by adding this definition at the top of your ImageUploader
file:
class Image; end
You could also do the same but in an initializer instead (ex. config/initializers/image.rb
).
This would make sure that the Image
class 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.
Solution 3
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]
Solution 4
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).
In config/application.rb:
config.image_sizes = {
thumb_width: 220,
preview_width: 460,
max_width: 960,
}.with_indifferent_access
In app/uploaders/image_uploader.rb:
version :thumb, from_version: :preview do
process resize_to_limit: [Rails.application.config.image_sizes[:thumb_width]]
end
Solution 5
Your ImageUploader
class is being autoloaded before THUMB_WIDTH
has been defined. Change the definition order:
class Image < ActiveRecord::Base
include Rails.application.routes.url_helpers
THUMB_WIDTH = 220
PREVIEW_WIDTH = 460
MAX_WIDTH = 960
mount_uploader :image, ImageUploader
end
Nemoden
Interested in: Photography, but I have no camera. Investing, but I have no money. Inventing, but I have no thoughts. Cooking, but I have no ingredients. Singing, but I have no voice. Making music, but I have no notes. Flying, but I have no wings.@Nemoden
Updated on July 09, 2022Comments
-
Nemoden almost 2 years
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
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.
-
Stoic over 10 yearsI do like what you have stated (as I was naive not to think of that :P ), but the only problem with this method is that we will have to do this for every model we want this uploader to handle, IMHO.
-
Nemoden over 10 yearsit says:
undefined local variable or method 'model'
-
Nemoden over 10 yearsCan I exploit
Rails::Application::Configuration
somehow? -
Nick Urban over 10 years@Nemoden Yes, see my answer.
-
Nemoden over 10 yearsA very thorough answer, a lot of useful information, good references. Thanks!
-
Nemoden over 10 yearsinterpreters! right! lines evaluated one-by-one. And I'm a 8 years-experienced developer (with a PHP background, but it doesn't really matter). Shame, shame, shame on me, how I could not see this in a first place? thanks!