2011-08-05 79 views
3

我正在寫一些有點像Facebook的共享鏈接預覽。可擴展處理程序/插件架構的Ruby結構

我想使它很容易就在一個新的文件拖放爲每一個新的網站,我想寫一個自定義的解析器新的網站擴展。我有設計模式的基本概念,但沒有足夠的模塊經驗來指定細節。我確信在其他項目中有很多類似的例子。

結果應該是這樣的:

> require 'link' 
=> true 
> Link.new('http://youtube.com/foo').preview 
=> {:title => 'Xxx', :description => 'Yyy', :embed => '<zzz/>' } 
> Link.new('http://stackoverflow.com/bar').preview 
=> {:title => 'Xyz', :description => 'Zyx' } 

和代碼將是這樣的:

#parsers/youtube.rb 
module YoutubeParser 
    url_match /(youtube\.com)|(youtu.be)\// 
    def preview 
    get_stuff_using youtube_api 
    end 
end 

#parsers/stackoverflow.rb 
module SOFParser 
    url_match /stachoverflow.com\// 
    def preview 
    get_stuff 
    end 
end 

#link.rb 
class Link 
    def initialize(url) 
    extend self with the module that has matching regexp 
    end 
end 

回答

3
# url_processor.rb 
class UrlProcessor 
    # registers url handler for given pattern 
    def self.register_url pattern, &block 
    @patterns ||= {} 
    @patterns[pattern] = block 
    end 

    def self.process_url url 
    _, handler = @patterns.find{|p, _| url =~ p} 
    if handler 
     handler.call(url) 
    else 
     {} 
    end 
    end 
end 

# plugins/so_plugin.rb 
class SOPlugin 
    UrlProcessor.register_url /stackoverflow\.com/ do |url| 
    {:title => 'foo', :description => 'bar'} 
    end 
end 

# plugins/youtube_plugin.rb 
class YoutubePlugin 
    UrlProcessor.register_url /youtube\.com/ do |url| 
    {:title => 'baz', :description => 'boo'} 
    end 
end 

p UrlProcessor.process_url 'http://www.stackoverflow.com/1234' 
#=>{:title=>"foo", :description=>"bar"} 
p UrlProcessor.process_url 'http://www.youtube.com/1234' 
#=>{:title=>"baz", :description=>"boo"} 
p UrlProcessor.process_url 'http://www.foobar.com/1234' 
#=>{} 

你只需要require從插件目錄每.RB。

+0

謝謝,這是一個比搞亂模塊,擴展和包括更簡單的方法,而不是。我還用另一種方法找到了一個教程[link](http://thomasjo.com/2010/12/15/really-simple-and-naive-ruby-plugin-framework/)。 –

0

如果你願意採取這種方式你應該掃描歸檔爲字符串然後include正確的一個。

在同樣的情況我嘗試了不同的方法。我用新方法擴展模塊,@@註冊它們,以便我不會註冊兩個相同名稱的方法。到目前爲止它運行良好,儘管我開始的這個項目遠沒有離開一個特定網站混亂的特定領域。

這是主文件。

module Onigiri 
    extend self 
    @@registry ||= {} 

    class OnigiriHandlerTaken < StandardError 
    def description 
     "There was an attempt to override registered handler. This usually indicates a bug in Onigiri." 
    end 
    end 

    def clean(data, *params) 
    dupe = Onigiri::Document.parse data 
    params.flatten.each do |method| 
     dupe = dupe.send(method) if @@registry[method] 
    end 
    dupe.to_html 
    end 

    class Document < Nokogiri::HTML::DocumentFragment 
    end 

    private 

    def register_handler(name) 
    unless @@registry[name] 
     @@registry[name] = true 
    else 
     raise OnigiriHandlerTaken 
    end 
    end 

end 

這裏是擴展文件。

# encoding: utf-8 
module Onigiri 
    register_handler :fix_backslash 
    class Document 
    def fix_backslash 
     dupe = dup 
     attrset = ['src', 'longdesc', 'href', 'action'] 
     dupe.css("[#{attrset.join('], [')}]").each do |target| 
     attrset.each do |attr| 
      target[attr] = target[attr].gsub("\\", "/") if target[attr] 
     end 
     end 
     dupe 
    end 
    end 
end 

我看到的另一種方法是使用一組不同的(但行爲上沒有區別)班的一個簡單的決策機制來調用正確的。一個包含類名和相應的url_matcher的簡單哈希可能就足夠了。

希望這會有所幫助。

0

我想我釘了它。

irb(main):001:0> require './url_handler' 
=> true 
irb(main):002:0> UrlHandler.new('www.youtube.com').process 
=> {:description=>"Nyan nyan!", :title=>"Youtube"} 
irb(main):003:0> UrlHandler.new('www.facebook.com').process 
=> {:description=>"Hello!", :title=>"Facebook"} 
irb(main):004:0> UrlHandler.new('www.stackoverflow.com').process 
=> {:description=>"Title fetcher", :title=>"Generic"} 

url_handler.rb:

class UrlHandler 
    attr_accessor :url 
    def initialize(url) 
    @url=url 
    if plugin=Module.url_pattern.find{|re, plugin| @url.match(re)} 
     extend plugin.last 
    else 
     extend HandlerPlugin::Generic 
    end 
    end 
end 

class Module 
    def url_pattern(pattern=nil) 
    @@patterns ||= {} 
    @@patterns[pattern] ||= self unless pattern.nil? 
    return @@patterns 
    end 
end 

module HandlerPlugin 
    module Generic 
    def process 
     {:title => 'Generic', :description => 'Title fetcher'} 
    end 
    end 
end 

Dir[File.join(File.dirname(__FILE__), 'plugins', '*.rb')].each {|file| require file } 

插件/ youtube.rb(facebook.rb是非常相似)

module HandlerPlugin::Youtube 
    include HandlerPlugin 
    url_pattern /youtube/ 
    def process 
    {:title => 'Youtube', :description => 'Nyan nyan!'} 
    end 
end 

它可能不是很好污染模塊這樣,但到目前爲止,這是我能想出的最佳解決方案。