0

我正在接受 ruby​​-kickstart (Josh Cheek) 挑战,即使我设法通过了所有测试,但有一件事我无法理解。

在练习中,您被要求覆盖实例变量的 << 方法。具体来说,这是您必须做的:

在 Ruby on Rails 中,当有人访问您网站上的 URL 时,您的应用程序会查看该 url,并将其映射到控制器方法以处理请求

我的老板希望能够根据正在访问的控制器方法来指定 HTML 输出的主体应该具有什么 CSS 类。我有责任提供一种方法,该方法在被调用时会返回一个可以处理请求的字符串。不过,有一些细微差别。返回的 String 必须在对象的整个生命周期中保留 方法必须能够被多次调用 返回的 String 应该知道如何添加新的类:每个类之间用空格分隔 唯一需要担心的方法是调用的是 << 方法。(加上一些其他不相关的东西)示例:

controller = ApplicationController.new   
controller.body_class                  
#=> ""    
controller.body_class << 'admin'    
controller.body_class     
#=> "admin"   
controller.body_class << 'category'    
controller.body_class        
#=> "admin category"    
controller.body_class << 'page' << 'order'    
controller.body_class                  
#=> "admin category page order"

我的工作解决方案:

class ApplicationController 

  def initialize
    @body_class = ""
  end

  def body_class
    def @body_class.<<(str)
      puts "self is:"+self
      return self if self=~/\b#{Regexp.escape(str)}\b/
      if self==""
        self.concat(str)
      else
        self.concat(" ")
        self.concat(str)
      end
    end

    return @body_class
  end
end

一切正常。但是我给出的较早的解决方案(但它没有用)如下

class ApplicationController 
  attr_accessor :body_class
  def initialize
    @body_class = ""
  end

  def @body_class.<<(str)
    puts "self is:"+self
    return self if self=~/\b#{Regexp.escape(str)}\b/


    if self==""
      self.concat(str)
    else
      self.concat(" ")
      self.concat(str)
    end

  end

  def body_class                #Even this for the way I work it out on my mind is unnecessary 
    return @body_class
  end



end

当有人在第二个不工作的解决方案上运行时,以下

obj = ApplicationController.new
obj.body_class << "Hi"

<< 方法没有被对象的单例覆盖。我不明白为什么我必须将单例方法包装在 body_class 方法中。(请注意,在第二个解决方案中有一个 attr_accessor。

任何人都可以启发我!谢谢!

4

2 回答 2

4

我不明白为什么我必须将单例方法包装在 body_class 方法中。

访问正确的实例变量。当您尝试在方法之外覆盖它时,您就处于类上下文中。此代码在类加载时运行。尚未创建任何实例。(即使此时确实存在实例,@body_class实例变量也属于 class ApplicationController,它本身就是 class 的实例Class)。

您需要实例上下文。


我也很确定这个问题可以在没有任何方法修补巫毒的情况下解决。只需提供不同的对象(符合相同的 API。这称为“鸭子类型”)。

class ApplicationController

  def body_class
    @body_class ||= CompositeClass.new
  end

  class CompositeClass
    def initialize
      @classes = []
    end

    def <<(new_class)
      @classes << new_class
    end

    # I imagine, this is what is ultimately called on ApplicationController#body_class,
    # when it's time to render the class in the html.
    def to_s
      @classes.join(' ')
    end
  end
end

自然没有测试此代码。

于 2017-09-21T07:10:32.310 回答
1

顺便说一句,正确的方法是显式地extend实例变量:

class A
  attr_reader :body_class
  def initialize
    @body_class = "".extend(Module.new do
      def <<(other)
        return self if self[/\b#{Regexp.escape(other)}\b/]
        concat(' ') unless empty?
        concat(other)
      end
    end)    
  end  
end
于 2017-09-21T07:14:28.657 回答