2

I just wrote my first lambda in Ruby and have run into an issue when using it with Rails.

I have a class called Material:

class Material < ApplicationRecord

  to_kilobytes = lambda { |bytes| bytes / 1000 }
  to_megabytes = lambda { |bytes| bytes / 1000000 }

  def size
    size = self.attachment_file_size

    if size < 999999
      to_kilobytes.call(size)
    elsif size >= 1000000
      to_megabytes.call(size)
    end
  end
end

I am trying to call this size method on my material object in order to find size conversion rates for files. The attachment_file_size method is provided by the Rubygem Paperclip.

When I try calling the size method on this material object in my show.html.erb file -

<span><%= @material.size %></span>

I am getting the following error.

undefined local variable or method `to_kilobytes' for Material:0x007fc6f6cba2e0

If I move the lambdas inside the size method it works fine. I realize this is a scoping error, I am just confused why these who lambdas are not able to be called on the size method since they are global in the class. Can somebody explain?

2
  • i don't think local variables outside of a method are ever accessible from the method. Commented Apr 21, 2018 at 4:27
  • What makes you think they are global? Hint: how does Ruby distinguish between different variable types? What does it mean when a variable starts with a lowercase letter? What does a global variable start with? Commented Apr 22, 2018 at 15:49

2 Answers 2

6

to_kilobytes and to_megabytes local variables are in the scope of class...end, but not inside the method size. Because each def..end creates a new scope in Ruby. Either make them instance methods, or create as constants and then u can use it without any scope issues. Like:

class Material < ApplicationRecord

  TO_KILOBYTES = lambda { |bytes| bytes / 1000 }
  TO_MEGABYTES = lambda { |bytes| bytes / 1000000 }

  def size
    size = self.attachment_file_size

    if size < 999999
      TO_KILOBYTES.call(size)
    elsif size >= 1000000
      TO_MEGABYTES.call(size)
    end
  end
end
Sign up to request clarification or add additional context in comments.

Comments

4

to_kilobytes and to_megabytes are class local variables, so they cannot be accessed in instance methods (size).


In Rails, we have a method called number_to_human_size, I think It will help you:

Formats the bytes in number into a more understandable representation (e.g., giving it 1500 yields 1.5 KB). This method is useful for reporting file sizes to users. You can customize the format in the options hash.

See number_to_human if you want to pretty-print a generic number.

Examples

number_to_human_size(123)                                          # => 123 Bytes
number_to_human_size(1234)                                         # => 1.21 KB
number_to_human_size(12345)                                        # => 12.1 KB
number_to_human_size(1234567)                                      # => 1.18 MB
number_to_human_size(1234567890)                                   # => 1.15 GB
number_to_human_size(1234567890123)                                # => 1.12 TB
number_to_human_size(1234567890123456)                             # => 1.1 PB
number_to_human_size(1234567890123456789)                          # => 1.07 EB
number_to_human_size(1234567, precision: 2)                        # => 1.2 MB
number_to_human_size(483989, precision: 2)                         # => 470 KB
number_to_human_size(1234567, precision: 2, separator: ',')        # => 1,2 MB
number_to_human_size(1234567890123, precision: 5)                  # => "1.1228 TB"
number_to_human_size(524288000, precision: 5)                      # => "500 MB"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.