2013-04-20 59 views
1
class Foo 
    def do_before 
    ... 
    end 

    def do_something 
    ... 

有没有办法在Foo类(如do_something)彼此方法之前运行do_before方法?如何在Sinatra中创建类似Rails的过滤器?

似乎Sinatra before块在每个HTTP请求之前运行,这与此类无关。

编辑:正如Michael在评论中指出的那样,Rails提供的唯一类似功能就是在Controller中。但是,Rails和Sinatra都提供了与此功能类似的功能。

+2

这是什么得到了与西纳特拉办?如果过滤器没有链接到HTTP请求,那么这是关于Ruby而不是任何框架。 – iain 2013-04-20 04:49:42

+1

是的,我不认为Rails提供这个。过滤器用于控制器请求。这听起来像是你正在做的事情可能不是最佳的。 – 2013-04-20 05:08:26

+0

@iain - 我在Sinatra工作,所以如果有Sinatra做这件事的方法,那么它会很有用。 – 2013-04-21 22:36:54

回答

5

由于iain在注释中指出,您指定的示例并不特定于Rails/Sinatra。我假设你喜欢Rails中的那些过滤器之前想,这是什么西纳特拉提供:

Sinatra的模块化应用:

class Foo < Sinatra::Base 

    before do 
    "Do something" 
    end 

    get '/' do 
    "Hello World" 
    end 
end 

class Bar < Sinatra::Base 

    before do 
    "Do something else" 
    end 

    get '/' do 
    "Hello World" 
    end 
end 

在你config.rb文件,

require 'foo.rb' 
require 'bar.rb' 

map '/foo' do 
    run Foo 
end 

map '/bar' do 
    run Bar 
end 

这是最接近Sinatra Rails控制器的类比。创建更多这样的类,你会得到类似的功能(类似,但可能不像你在Rails世界中所期望的那样)。

3

您还可以使用一些元编程来创建一个之前的过滤器。例如:

class Foo 
    def method_1; p "Method 1"; end 
    def method_2; p "Method 2"; end 
    def preprocess_method; p "Pre-Processing method"; end 

    def self.before_filter m 
    current_methods = instance_methods(false) - [m] 
    self.new.instance_eval do 
     current_methods.each do |meth| 
     inst_method = public_method(meth) 
     self.class.send :define_method,inst_method.name do 
      public_send m 
      inst_method.call 
     end 
     end 
    end 
    end 
    before_filter :preprocess_method 
end 

o = Foo.new 
o.method_1 
#output: 
"Pre-Processing method" 
"Method 1" 

o.method_2 
#outputs 
"Pre-Processing method" 
"Method 2" 

在这种情况下,preprocess_method(即上您的示例do_before)将每次调用Foo类中定义的任何实例的方法之前被调用。

3

不知道你在做什么使得它很难知道如何回答,但增加了信息在那里在网络上;)我给@ fmendez的答案替代:

module Filterable 
    def self.included(base) 
    base.extend ClassMethods 
    end 
    module ClassMethods 
    def do_before(name, &block) 
     before_filters[name.to_sym] = block 
    end 
    def before_filters 
     @before_filters ||= {} 
    end 
    def method_added(name) 
     return if name.to_s.start_with?("unfiltered_") 
     return if before_filters.has_key? name 
     before_filters[name.to_sym] ||= nil 
     alias_method "unfiltered_#{name}", name 
     define_method name do |*args,&block| 
     self.class.before_filters[name.to_sym].call if self.class.before_filters[name.to_sym] 
     send "unfiltered_#{name}", *args, &block 
     end 
    end 
    end 
end 

class Foo 
    include Filterable 

    def something(x) 
    x * 3 
    end 

    do_before :something do 
    puts "Before…" 
    end 
end 

Foo.new.something 4 

输出:

之前...
#=> 12

+0

这实际上是目前为止的最佳答案。执行之前过滤器的模块是完美的。看起来这段代码确实有点复杂,并且不需要复杂的... – 2013-04-21 22:42:23

+0

@BSeven我有点困惑,你说这是最好的答案,但不需要复杂性?无论哪种方式,我认为你需要充实你的问题,你想达到什么目的,如果它不是一个链接到请求的[filter](http://www.sinatrarb.com/intro#Filters),什么是它? – iain 2013-04-21 23:37:13

相关问题