Ruby中attr_accessor在Proc内无法正常访问的技术问题
Ruby中类变量在闭包proc里无法访问的解决方案
你遇到的问题是:通过attr_accessor定义的类属性size,在content_length_proc中赋值后,progress_proc里无法访问到这个值。先看一下问题代码里的关键坑点:
require 'open-uri' MEGABYTE = 1024.0 * 1024.0 def bytes_to_megabytes(bytes) bytes / MEGABYTE end class Downloader class << self attr_accessor :size def get(resource) open(resource, content_length_proc: proc do |t| size = bytes_to_megabytes(t).round puts "Total size is: #{size}" end, progress_proc: proc do |step| # size 无法在此处打印! puts "Downloading #{bytes_to_megabytes(step).round} out of #{size}" end ) end end end
问题根源
- setter方法调用错误:在
content_length_proc里写size = ...时,Ruby会把它当成局部变量赋值,而不是调用attr_accessor生成的size=类方法。你根本没把值存到类的size属性里。 - 闭包作用域问题:就算你正确赋值了,
progress_proc的上下文里self并不是Downloader类,直接写size会去当前proc的局部变量里找,自然找不到类属性的值。
修复后的代码
只需要两处小修改,就能解决问题:
require 'open-uri' MEGABYTE = 1024.0 * 1024.0 def bytes_to_megabytes(bytes) bytes / MEGABYTE end class Downloader class << self attr_accessor :size def get(resource) open(resource, content_length_proc: proc do |t| # 显式调用self.size=来触发类的setter方法 self.size = bytes_to_megabytes(t).round puts "Total size is: #{self.size}" end, progress_proc: proc do |step| # 通过类名访问类属性,确保拿到正确的值 puts "Downloading #{bytes_to_megabytes(step).round} out of #{Downloader.size}" end ) end end end
修复说明
- 第一处修改:把
size = ...改成self.size = ...,这样Ruby才会调用attr_accessor生成的类setter方法,把值真正存在Downloader类的size属性里。 - 第二处修改:把
progress_proc里的size改成Downloader.size,明确指定访问类的属性,避免闭包作用域的干扰。
这样修改后,progress_proc就能正常获取到总文件大小,正确输出下载进度了。
内容的提问来源于stack exchange,提问作者stratis
相关产品推荐
相关产品推荐

