ActiveResource and YouTube  8


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.

Filed in ruby
Tagged as activeresource api 
Posted on 15 January
8 comment Bookmark   AddThis Social Bookmark Button

archive

ActiveResource and YouTube

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