我写的东西有点像Facebook的共享链接预览。
我想通过为每个我想编写自定义解析器的新网站添加一个新文件,使其可以轻松扩展到新网站。我已经找到了设计模式的基本概念,但没有足够的模块经验来确定细节。我确信在其他项目中有很多类似的例子。
结果应该是这样的:
> 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
答案 0 :(得分: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。
答案 1 :(得分:0)
如果您愿意采用这种方法,您应该扫描字段中的mathing字符串,然后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的简单哈希可能就足够了。
希望这有帮助。
答案 2 :(得分: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 }
plugins / youtube.rb(facebook.rb非常相似)
module HandlerPlugin::Youtube
include HandlerPlugin
url_pattern /youtube/
def process
{:title => 'Youtube', :description => 'Nyan nyan!'}
end
end
像这样污染模块可能不太好,但到目前为止,这是我能想到的最好的解决方案。