Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用Ruby实现像嵌套在过滤器周围一样的面向APEC的?_Ruby_Design Patterns_Filter_Aop - Fatal编程技术网

用Ruby实现像嵌套在过滤器周围一样的面向APEC的?

用Ruby实现像嵌套在过滤器周围一样的面向APEC的?,ruby,design-patterns,filter,aop,Ruby,Design Patterns,Filter,Aop,我试图编写一个支持嵌套在过滤器周围的类,而不引入面向方面的库 class Foo attr_accessor :around_filter def initialize #filters which wrap the following one are the ones with interesting logic #vanilla do-nothing filter @around_filter = lambda { yield } # or lambda {

我试图编写一个支持嵌套在过滤器周围的类,而不引入面向方面的库

class Foo
  attr_accessor :around_filter

  def initialize
    #filters which wrap the following one are the ones with interesting logic
    #vanilla do-nothing filter
    @around_filter = lambda { yield } # or lambda {|&blk| blk.call}
  end

  def bar
    #would execute the around filters however deeply nested, then "meaty code"
    @around_filter.call do
      #meaty code here
      puts 'done' 
    end
  end

  #I expected to point only to the topmost filter, hoping to recurse
  def add_around_filter(&blk)
    prior_around_filter = @around_filter
    @around_filter = #??mystery code here?? refers to prior_around_filter
  end
end
目标是能够添加任意数量的环绕过滤器:

foo = Foo.new
foo.add_around_filter do
  puts 'strawberry'
  yield
  puts 'blueberry'
end
foo.add_around_filter do
  puts 'orange'
  yield
  puts 'grape'
end
foo.bar #=> orange, strawberry, done, blueberry, grape
我知道这个例子中有很多漏洞。我从一个更大的实际课程中提炼出来的东西,只够考维大致方向

虽然我更喜欢
yield
语法,但我并不反对块引用:

foo.add_around_filter do |&blk|
  puts 'orange'
  blk.call
  puts 'grape'
end

我只使用了一个环绕过滤器。我尝试了很多筑巢的方法,但始终没有解开谜团。如果你有解决办法,我会非常感激

一种可能性是明确指定每个过滤器的“之前”和“之后”部分:

代码
class-Foo
def初始化
@前\u过滤器=Array.new
@后过滤器=Array.new
结束
def棒
@在| filters.each{| f | f.call}之前
“完成”
@在| filters.each{| f | f.call}之后
结束
def添加过滤器(选项)
@before\u filters.insert(0,选项[:before])
@后过滤器>foo=foo.new
>>添加过滤器(:before=>lambda{puts”“},
:after=>lambda{put”“})
>>添加过滤器(:before=>lambda{puts”“},
:after=>lambda{put”“})
>>美食酒吧
完成

过滤器中的
屈服
将尝试屈服于在其定义点定义的块,这不是您想要的。它将使用显式块形式(见1.9),如下所示:

class Foo
  attr_accessor :around_filter

  def initialize
    #filters which wrap the following one are the ones with interesting logic
    #vanilla do-nothing filter
    @around_filter = Proc.new{|&blk| blk.call }
  end

  def bar
    #would execute the around filters however deeply nested, then "meaty code"
    @around_filter.call do
      #meaty code here
      puts 'done' 
    end
  end

  #I expected to point only to the topmost filter, hoping to recurse
  def add_around_filter(&filter)
    prior_around_filter = @around_filter
    @around_filter = Proc.new do |&blk|
      filter.call do
        prior_around_filter.call(&blk)
      end
    end
  end
end

foo = Foo.new
foo.add_around_filter do |&blk|
  puts 'strawberry'
  blk.call
  puts 'blueberry'
end
foo.add_around_filter do |&blk|
  puts 'orange'
  blk.call
  puts 'grape'
end
foo.bar #=> orange, strawberry, done, blueberry, grape

您甚至可以在ruby 1.8.X中使用中间件模仿Rack的行为:

class Builder
  def initialize
    @filters = []
    @app = nil
  end

  def add_filter filter
    @filters << proc { |app| filter.new(app)}
  end

  def run app
    @app=app
  end

  def build
    app = @app
    @filters.reverse.each do  |filter|
      app = filter.call(app)
    end
    @app = app
  end
  def go(env)
    build
    @app.call(env)
  end
end


class FilterBase
  def initialize(app)
    @app = app
  end

  def call(env)
    puts ">>> BEFORE"
    @app.call(env)
    puts "<<< AFTER"
  end
end

class FilterSecond
  def initialize(app)
    @app = app
  end

  def call(env)
    puts "<hello>"
    @app.call(env)
    puts "</hello>"
  end
end

class FilterThird
  def initialize(app)
    @app = app
  end

  def call(env)
    env[:feeling]=:happy
    @app.call(env)
  end
end

class DummyApp
  def call(env)
    puts "HELLO #{env.inspect}" 
  end
end


b = Builder.new
b.add_filter FilterBase
b.add_filter FilterSecond
b.add_filter FilterThird
b.run DummyApp.new
b.go({:feeling => :bad})
类生成器
def初始化
@过滤器=[]
@app=零
结束
def添加过滤器
@过滤器:坏})
将导致

>>> BEFORE
<hello>
HELLO {:feeling=>:happy}
</hello>
<<< AFTER
>>之前
你好{:feeling=>:happy}

如果您想使用像这样的AOP库,那么可以保存大量样板代码,另外还可以单独测试方面。在不使用任何库的情况下,下面的代码将执行您想要的操作

class A
  def bar input
    puts input
    input.upcase
  end

  def self.add_around_filter target, &block
    @filter_count ||= 0
    @filter_count += 1
    filter_method = "#{target}_around_filter_#{@filter_count}"
    define_method filter_method, block

    orig_method = instance_method(target)
    define_method target do |*args, &block|
      send filter_method, orig_method.bind(self), *args, &block
    end
  end
end

A.add_around_filter :bar do |proxy, *args, &block|
  puts 'before'
  result = proxy.call(*args, &block)
  puts 'after'
  result
end

A.add_around_filter :bar do |proxy, *args, &block|
  puts 'before 2'
  result = proxy.call(*args, &block)
  puts 'after 2'
  result
end

puts A.new.bar 'abc'

# Output
#before 2
#before
#abc
#after
#after 2
#ABC

这实际上是我当前的实现,但不幸的是,我需要使用around_过滤器。不过,谢谢。:)那一点点代码相当难以捉摸。非常感谢,马克·安德烈!帮了大忙!Ruby 1.9中非常微妙的块语义变化产生了多么大的影响,这令人惊讶。
>>> BEFORE
<hello>
HELLO {:feeling=>:happy}
</hello>
<<< AFTER
class A
  def bar input
    puts input
    input.upcase
  end

  def self.add_around_filter target, &block
    @filter_count ||= 0
    @filter_count += 1
    filter_method = "#{target}_around_filter_#{@filter_count}"
    define_method filter_method, block

    orig_method = instance_method(target)
    define_method target do |*args, &block|
      send filter_method, orig_method.bind(self), *args, &block
    end
  end
end

A.add_around_filter :bar do |proxy, *args, &block|
  puts 'before'
  result = proxy.call(*args, &block)
  puts 'after'
  result
end

A.add_around_filter :bar do |proxy, *args, &block|
  puts 'before 2'
  result = proxy.call(*args, &block)
  puts 'after 2'
  result
end

puts A.new.bar 'abc'

# Output
#before 2
#before
#abc
#after
#after 2
#ABC