This article is about consuming YouTube API in your Ruby/Rails project using ActiveResource. Moreover, this article is an example of how to extend ActiveResource to consume non rest-style API.
Benefits of using our extension to ActiveResource :
- ActiveResource provides a ActiveRecord style interface.
- You can modify our extension according to your interface requirement.
- No not need to use and rely on Ruby library for YouTube REST API.
YouTube references
http://code.google.com/apis/youtube/developers_guide_protocol.html
http://code.google.com/apis/youtube/reference.html
Note : The extension is using ActiveResource 2.0.2
ActiveResource extension
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
require 'rubygems' require 'activeresource' class ActiveYouTube < ActiveResource::Base class << self ## Remove format from the url. def element_path(id, prefix_options = {}, query_options = nil) prefix_options, query_options = split_options(prefix_options) if query_options.nil? "#{prefix(prefix_options)}#{collection_name}/#{id}#{query_string(query_options)}" end ## Remove format from the url. def collection_path(prefix_options = {}, query_options = nil) prefix_options, query_options = split_options(prefix_options) if query_options.nil? "#{prefix(prefix_options)}#{collection_name}#{query_string(query_options)}" end ## For a collection call, ActiveResource formatting is not ## compliant with YouTube's output. def instantiate_collection(collection, prefix_options = {}) unless collection.kind_of? Array [instantiate_record(collection, prefix_options)] else collection.collect! { |record| instantiate_record(record, prefix_options) } end end ## To convert output into proper standard. ## If single element is present in output then entry is not an array. ## So this method will ensure entry is always an array. alias :old_find :find def find(*args) output=old_find(*args) if output.respond_to?:entry and !(output.entry.kind_of? Array) output.entry=[output.entry] end output end ## When using ActiveResource::CustomMethods, ActiveResource first tries to retrieve the id using find() ## and then makes a get() call using that id. ## But, youtube returns the url of this item as id, which we don't want. This method overrides the behavior. ## Example: comments = Video.find_custom("ZTUVgYoeN_o").get(:comments) def find_custom(arg) object = self.new object.id = arg object end ## Following method from ActiveResource::CustomMethods extends the capabilities of activeresource for non-standard urls ;-) ## The objects returned from this method are not automatically converted into ActiveResource instances - they are ordinary Hashes. ## Modifications below ensures that you get ActiveResource instances. def get(method_name, options = {}) object_array = connection.get(custom_method_collection_url(method_name, options), headers) if object_array.class.to_s=="Array" object_array.collect! {|record| self.class.new.load(record)} else self.class.new.load(object_array) end end end ## Instance Methods: (modifying the ActiveRecord::CustomMethods). ## This modification is same as defined in above method def get(method_name, options = {}) self.class.new.load(connection.get(custom_method_element_url(method_name, options), self.class.headers)) end ## Modifying the url formation to make it Youtube API complaint def custom_method_element_url(method_name, options = {}) "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/" + "#{method_name}#{self.class.send!(:query_string, options)}" end end |
- When using the ActiveResource::CustomMethods (get/post) make sure you are using "find_custom" and not "find"
- Disable the logging of ActiveResource, since the following line leads to exception if logger is enabled.
1 2 |
## line 111, connection.rb logger.info "--> #{result.code} #{result.message} (#{result.body ? result.body : 0}b %.2fs)" % time if logger |
Example usage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
#### Create classes for YouTube resources. class Video < ActiveYouTube self.site = "http://gdata.youtube.com/feeds/api" ## To search by categories and tags def self.search_by_tags (*options) from_urls = [] if options.last.is_a? Hash excludes = options.slice!(options.length-1) if excludes[:exclude].kind_of? Array from_urls << excludes[:exclude].map{|keyword| "-"+keyword}.join("/") else from_urls << "-"+excludes[:exclude] end end from_urls << options.find_all{|keyword| keyword =~ /^[a-z]/}.join("/") from_urls << options.find_all{|category| category =~ /^[A-Z]/}.join("%7C") from_urls.delete_if {|x| x.empty?} self.find(:all,:from=>"/feeds/api/videos/-/"+from_urls.reverse.join("/")) end end class User < ActiveYouTube self.site = "http://gdata.youtube.com/feeds/api" end class Standardfeed < ActiveYouTube self.site = "http://gdata.youtube.com/feeds/api" end class Playlist < ActiveYouTube self.site = "http://gdata.youtube.com/feeds/api" end ##### Examples ####### #### VIDEO ## search for videos search = Video.find(:first, :params => {:vq => 'ruby', :"max-results" => '5'}) puts search.entry.length ## video information of id = ZTUVgYoeN_o vid = Video.find("ZTUVgYoeN_o") puts vid.group.content[0].url ## video comments comments = Video.find_custom("ZTUVgYoeN_o").get(:comments) puts comments.entry[0].link[2].href ## searching with category/tags results = Video.search_by_tags("Comedy") puts results[0].entry[0].title # more examples: # Video.search_by_tags("Comedy", "dog") # Video.search_by_tags("News","Sports","football", :exclude=>["Comedy","soccer"]) # Video.search_by_tags("News","Sports","football", :exclude=>"soccer") #### STANDARDFEED ## retrieving standard feeds most_viewed = Standardfeed.find(:most_viewed, :params => {:time => 'today'}) puts most_viewed.entry[0].group.content[0].url #### USER ## user's profile - guthrie user_profile = User.find("guthrie") puts user_profile.link[1].href ## user's playlist - john user_playlist = User.find_custom("john").get(:playlists) puts user_playlist.link[1].href ## user's upload or favorites rick_video = User.find_custom("rick").get(:uploads) puts rick_video.entry[0].group.content[0].url ## user's subscription user_subscriptions = User.find_custom("guthrie").get(:subscriptions) puts user_subscriptions.to_yaml #### PLAYLIST ## get playlist - multiple elements in playlist playlist = Playlist.find("EBF5D6DC4589D7B7") puts playlist.entry[0].group.content[0].url ## get playlist - single element in playlist playlist = Playlist.find("45C563323B344971") puts playlist.entry[0].group.content[0].url |

This is awsome, kinda hard for me to fully understand it at a first approach. But I just tested this and wow, my head is full of ideas now that it’s hard to sort them xD.
Very nice, really.
This looks pretty cool. You might want to consider bundling this up as a gem, to more easily share the love.
Hi Josh,
Hmnnn… it just didn’t strike to me that it could be turned into a gem. Surely will look into it.
Thanks!
This looks promising, but I’m still trying to get it to work. I made an activeyoutube.rb file, but where do I place it in my rails directory so that rails finds the class ActiveYouTube. Thanks!
@Brian,
If you are getting “load_missing_constant” error. Please rename activeyoutube.rb file to active_you_tube.rb, and it should work.
Also, If you can wait for a day or two, we are planning to release this as a gem.
Hi
How do I use it? require ‘active_youtube’? That doesn’t seem to work.
@eric:
Please refer http://www.quarkruby.com/2008/2/12/active-youtube to use this as gem.