I’m writing my first RoR app and currently I’m working on allowing users to upload images. I’m using Paperclip for this purpose. One of the steps involves adding has_attached_file to my model:
class MyModel < ActiveRecord::Base
#...
has_attached_file :picture, styles: {
large: "120x150#>",
small: "60x75#>"
}
#...
end
If I do it like this, everything works smoothly (or so it seems). But I will also need to access the same constant values as integers somewhere else, so I’ve added a hash:
class MyModel < ActiveRecord::Base
#...
has_attached_file :picture, styles: {
large: "120x150#>",
small: "60x75#>"
}
def picture_sizes
{
large: {width: 120, height: 150},
small: {width: 60, height: 75}
}
end
#...
end
This creates an ugly redundancy. So I thought about writing a method generating the first hash from the second one, like this
class MyModel < ActiveRecord::Base
#...
has_attached_file :picture, styles: picture_sizes_as_strings
def picture_sizes
{
large: {width: 120, height: 150},
small: {width: 60, height: 75}
}
end
def picture_sizes_as_strings
result = {}
picture_sizes.each do |label, size|
result[:label] = "#{size[:width]}x#{size[:height]}#>"
end
return result
end
#...
end
But this rises an error:
undefined local variable or method `picture_sizes_as_strings' for #<Class:0x007fdf504d3870>
What am I doing wrong?
The problem is that you’re trying to use an instance method
picture_sizes_as_stringsin a declaration (has_attached_image) that’s run on the class level. It’s the difference between callingand
In the first case we are referring to a class method (a method on the class MyModel itself) and in the second case we are referring to an instance method (a method on the individual my_model object.)
So first of all you have to change the methods to class methods by prefixing their names with
self., so:Now that doesn’t completely fix your problem yet, because
has_attached_imageis processed when the model is first parsed by ruby. That means it will try to runhas_attached_imagebefore you defineself.picture_sizesso it will still sayundefined method.You can fix this by putting
self.picture_sizesbefore thehas_attached_filedeclaration but that’s quite ugly. You could also put the data in a constant but that has its own problems.Honestly there’s no really pretty way to fix this. If it were me I’d probably reverse the whole process, define the styles as normal and then use a method to convert the strings to integers, something like this:
Then you can call
MyModel.first.numeric_sizes(:medium)to find out the sizes for a specific style, returned as an array. Of course you could change them into a hash too or whatever format you need.