问题
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?
回答1:
The problem is that you're trying to use an instance method picture_sizes_as_strings
in a declaration (has_attached_image
) that's run on the class level. It's the difference between calling
MyModel.picture_sizes_as_strings
and
MyModel.first.picture_sizes_as_strings
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:
def self.picture_sizes
{
large: {width: 120, height: 150},
small: {width: 60, height: 75}
}
end
Now that doesn't completely fix your problem yet, because has_attached_image
is processed when the model is first parsed by ruby. That means it will try to run has_attached_image
before you define self.picture_sizes
so it will still say undefined method
.
You can fix this by putting self.picture_sizes
before the has_attached_file
declaration 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:
class MyModel < ActiveRecord::Base
has_attached_file :picture, styles: {
large: "120x150#>",
small: "60x75#>"
}
def numeric_sizes style
# First find the requested style from Paperclip::Attachment
style = self.picture.styles.detect { |s| s.first == style.to_sym }
# You can consolidate the following into one line, I will split them for ease of reading
# First strip all superfluous characters, we just need the numerics and the 'x' to split them
sizes = style.geometry.gsub(/[^0-9x]/,'')
# Next split the two numbers across the 'x'
sizes = sizes.split('x')
# Finally convert them to actual integer numbers
sizes = sizes.map(&:to_i)
end
end
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.
回答2:
the has_attached_file
is evaluated at Runtime. You've defined an instance method, but you aren't calling the method from an instance context.
Try:
def self.picture_sizes_as_strings
# code here
end
Make sure you define the other method with self.
also
and then:
has_attached_file :picture, :styles => picture_sizes_as_strings
should work.
来源:https://stackoverflow.com/questions/12132991/ruby-on-rails-passing-a-returned-value-of-a-method-to-has-attached-file-do-i