ActiveResource and YouTube  7


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 :

  1. ActiveResource provides a ActiveRecord style interface.
  2. You can modify our extension according to your interface requirement.
  3. No not need to use and rely on Ruby library for YouTube REST API.

YouTube references

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
Points to keep in mind
- 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
Filed in ruby
Tagged as activeresource api 
Posted on 15 January
7 comment Bookmark   AddThis Social Bookmark Button
Comments

Leave a response

  1. Edgar J. SuárezJanuary 25, 2008 @ 09:46 PM

    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.

  2. Josh NicholsJanuary 26, 2008 @ 04:27 AM

    This looks pretty cool. You might want to consider bundling this up as a gem, to more easily share the love.

  3. QuarkJanuary 26, 2008 @ 08:23 PM

    Hi Josh,

    Hmnnn… it just didn’t strike to me that it could be turned into a gem. Surely will look into it.

    Thanks!

  4. BrianFebruary 09, 2008 @ 07:16 PM

    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!

  5. QuarkFebruary 09, 2008 @ 08:06 PM

    @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.

  6. ericMarch 24, 2008 @ 09:31 PM

    Hi

    How do I use it? require ‘active_youtube’? That doesn’t seem to work.

  7. QuarksMarch 25, 2008 @ 06:42 AM

    @eric:

    Please refer http://www.quarkruby.com/2008/2/12/active-youtube to use this as gem.

Comment