Youtube検索
WEB+DB PRESS Vol.44より、MVCの解説としてYoutubeから検索するプログラムをPerlで例として掲載されていたのをRubyで書き直してみた。
忘れないようにメモ。
少しだけ簡単にはしたけれど、まあほぼ同じ。
index.cgi
#!/home/rightgo09/bin/ruby # -*- coding: utf-8 -*- require 'cgi' $LOAD_PATH.unshift('lib') require 'myapp' q = CGI.new app = MyApp.new app.request = q app.path = q.path_info res = app.dispatch print q.header({ 'charset' => 'UTF-8', 'type' => res.content_type, }); print res.content __END__
lib/myapp.rb
require 'myapp/view' require 'myapp/response' require 'myapp/controller' class MyApp attr_accessor :request, :path def dispatch con = self.controller con.req = self.request con.res = Response.new con.view = View.new({ 'path_segments' => [ self.path_segments ] }) con.handle_request return con.res end # get controller instance def controller handler = ['myapp','controller', self.path_segments].join('/') require handler handler = self.get_class(self.get_classname(self.path_segments)) return handler.new end def path_segments self.path ||= '/' path_segments = self.path.split('/') path_segments.shift path_segments.push('index') if path_segments.size == 0 return path_segments end def get_classname(path_segments = Array.new) return ['controller', path_segments].join('::').gsub(/\w+/) do |m| m[0] = m[0,1].upcase; m end end def get_class(classname) return classname.split(/::/).inject(Object) do |c,name| c.const_get(name) end end end
lib/myapp/response.rb
class Response attr_accessor :content_type, :content end
lib/myapp/view.rb
require 'erb' include ERB::Util class View attr_accessor :path_segments def initialize(args) self.path_segments = args['path_segments'] @vars = Hash.new end def vars(args = nil) if args.nil? then return @vars else @vars.merge!(args) end end def template_file file = ['template', self.path_segments].join('/') file << '.html' return file end def render erb = ERB.new(File.read(self.template_file)) vars = self.vars out = erb.result(binding()) return out end end
うーん、bindingがいちいちローカル変数に落とし込んでおかないといけないのが気持ち悪い。
lib/myapp/model/youtube.rb
require 'open-uri' require 'nokogiri' require 'myapp/model/youtube/result' YOUTUBE_API = 'http://gdata.youtube.com/feeds/api/videos?orderby=viewCount&vq=' class Model class Model::Youtube attr_accessor :title, :link, :thumbnail_url def initialize(args = Hash.new) self.title = args[:title] self.link = args[:link] self.thumbnail_url = args[:thumbnail_url] end def Youtube.search(query = '') uri = URI(YOUTUBE_API + URI.encode(query)) entries = self._parse_videos(uri.read) return Model::Youtube::Result.new(entries) end def Youtube._parse_videos(content) doc = Nokogiri::XML(content) entries = Array.new doc.search('entry').each { |entry| entries.push(Model::Youtube.new({ :title => entry.search('title').text, :link => entry.xpath('media:group/media:player').first['url'], :thumbnail_url => entry.xpath('media:group/media:thumbnail').first['url'], })); } return entries end def to_hash return { :title => self.title, :link => self.link, :tumbnail_url => self.thumbnail_url, } end end end
lib/myapp/model/youtube/result.rb
class Model class Model::Youtube class Model::Youtube::Result attr_accessor :entries def initialize(entries) self.entries = entries end end end end
lib/myapp/controller.rb
class Controller attr_accessor :req, :res, :view def handle_request self.do_task if self.res.content.nil? then # default content is rendering view self.res.content = self.view.render end self.res.content_type ||= 'text/html' # default content-type return self.res end end
lib/myapp/controller/index.rb
class Controller::Index < Controller def do_task end end
lib/myapp/controller/search.rb
require 'myapp/model/youtube' class Controller::Search < Controller def do_task self.view.vars({ 'result' => Model::Youtube.search(self.req['q']), 'q' => self.req['q'], }) end end
template/index.html
<html> <head> <title>YouTube Search</title> <style type="text/css"> ul li { list-style-type : none; display : inline; } </style> </head> <body> <h1>YouTube Search</h1> <form method="get" action="./index.cgi/search"> <input type="text" name="q" value="" /> <input type="submit" value="search" /> </form> </body> </html>
template/search.html
<%#-*- coding: UTF-8 -*-%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>YouTube Search - Result</title> <style type="text/css"> ul li { list-style-type : none; display : inline; } </style> </head> <body> <h1>YouTube Search</h1> <form method="get"> <input type="text" name="q" value="<%=h vars['q'] %>" /> <input type="submit" value="search" /> </form> <h2>検索結果 (<%= vars['result'].entries.size %>)</h2> <ul> <% for entry in vars['result'].entries %> <li><a href="<%= entry.link %>"> <img src="<%= entry.thumbnail_url %>" title="<%=h entry.title %>" /></a></li> <% end %> </ul> </body> </html>
先頭にUTF-8だと入れないとerbの置き換えで死ぬ。
その解決策でえらく時間かかった。
こんな感じ。