howcast-howcast 0.4.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,60 @@
1
+ == 0.4.7 2009-8-19
2
+
3
+ * Bump the version number so github will build this
4
+
5
+ == 0.4.6 2009-08-19
6
+
7
+ * Move gem to github, use echoe to clean up configuration, remove dependency on hpricot
8
+
9
+ == 0.4.5 2009-07-27
10
+
11
+ * Removed guide wrappers, Howcast API just has how-to's which merges old video and guides
12
+
13
+ == 0.4.3 2009-03-02
14
+
15
+ * Compatibility with ruby 1.9 - replacing dependency on hpricot with why-hpricot as github as the ruby 1.9 hpricot gem
16
+
17
+ == 0.4.2 2009-02-04
18
+
19
+ * Adding embed code to video object
20
+
21
+ == 0.4.1 2008-11-05
22
+
23
+ * Support for finding a category ancestor trail from a category object
24
+
25
+ == 0.4 2008-11-02
26
+
27
+ * Add support for the new category API to get the name and parent ID
28
+ * Added badges and easy_steps methods for video
29
+
30
+ == 0.3.4 2008-08-21
31
+
32
+ * Support for new paging scheme in browse pages - ?page=1 => /1
33
+
34
+ == 0.3.3 2008-07-25
35
+
36
+ * Video dimensions
37
+
38
+ == 0.3.2 2008-06-03
39
+
40
+ * Clean up with inject
41
+
42
+ == 0.3.1 2008-04-08
43
+
44
+ * Ability to search in extended mode
45
+
46
+ == 0.3 2008-03-27
47
+
48
+ * API Key support
49
+
50
+ == 0.2 2008-03-11
51
+
52
+ * Support for WikiGuide API - list and search
53
+
54
+ == 0.1.1 2008-03-04
55
+
56
+ * Minor bug fixes and documentation
57
+
58
+ == 0.1.0 2008-02-19
59
+
60
+ * Initial release
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Howcast Media Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,20 @@
1
+ CHANGELOG
2
+ howcast.gemspec
3
+ lib/howcast/client/base.rb
4
+ lib/howcast/client/category.rb
5
+ lib/howcast/client/search.rb
6
+ lib/howcast/client/video.rb
7
+ lib/howcast/client.rb
8
+ lib/howcast/errors.rb
9
+ lib/howcast.rb
10
+ License.txt
11
+ Manifest
12
+ Rakefile
13
+ README.markdown
14
+ spec/howcast/client/base_spec.rb
15
+ spec/howcast/client/category_spec.rb
16
+ spec/howcast/client/search_spec.rb
17
+ spec/howcast/client/video_spec.rb
18
+ spec/spec.opts
19
+ spec/spec_helper.rb
20
+ tasks/github.rake
@@ -0,0 +1,46 @@
1
+ # Howcast API Ruby Wrapper
2
+
3
+ Copyright (c) 2008 Howcast Media Inc.
4
+
5
+ Author: Jingshen Jimmy Zhang <jimmy@howcast.com>
6
+
7
+ ## Installing
8
+
9
+ sudo gem install howcast
10
+
11
+ ## Example
12
+
13
+ require 'rubygems'
14
+ require 'howcast'
15
+
16
+ hc = Howcast::Client.new(:key => "INSERT API KEY HERE")
17
+ # Will print out the video titles of the first page of recent howcast studios videos
18
+ puts "Recent Howcast Studios Videos"
19
+ hc.videos.each do |v|
20
+ puts v.title
21
+ end
22
+
23
+ puts "2nd Page of Top Rated Videos"
24
+ # Will print out the video titles of the 2nd page of top rated videos
25
+ hc.videos(:page => 2, :sort => "top_rated", :filter => "all").each do |v|
26
+ puts v.title
27
+ end
28
+
29
+ puts "Videos matching 'origami'"
30
+ hc.search("origami").each do |v|
31
+ puts v.title
32
+ end
33
+
34
+ puts "Video with id 946"
35
+ puts hc.video(946).title
36
+
37
+ # Category API
38
+ piano = hc.category(1105)
39
+ puts "The parent category of Piano is #{hc.category(piano.parent_id).name}"
40
+
41
+ ancestors = piano.parents.map{|c| c[:name]}
42
+ # Ancestors will be an array of hash metadata:
43
+ # => [{:name=>"Performing Arts", :id=>"1048"},
44
+ # {:name=>"Musical Instruments", :id=>"1095"},
45
+ # {:name=>"Keyboards", :id=>"1103"}]
46
+ puts "The ancestors of piano are: #{ancestors.join(" -> ")}"
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('howcast', '0.4.7') do |x|
6
+ x.summary = "Howcast API Ruby Wrapper"
7
+ x.description = <<-EOS
8
+ Howcast offers an Application Programming Interface (API) which allows developers to build applications that interface with Howcast. The Howcast API is RESTful (REpresentational State Transfer) and users of this API will be able: 1) Retreive detailed information about a single video, including metadata such as title, description, video views, rating etc; 2) Retrieve a list of videos restricted by a set of filters offered by Howcast and sorted using several metrics that you can specify (most recent, most views, etc); 3) Search for video; 4) And much more. Note: Before you can use our APIs, you must register an API key, that is submitted with each request.
9
+ EOS
10
+ x.url = "http://github.com/howcast/howcast-gem"
11
+ x.author = "Jingshen Jimmy Zhang"
12
+ x.email = "jimmy@howcast.com"
13
+ x.ignore_pattern = ["tmp/*", "script/*"]
14
+ x.development_dependencies = []
15
+ end
16
+
17
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{howcast}
5
+ s.version = "0.4.7"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Jingshen Jimmy Zhang"]
9
+ s.date = %q{2009-08-19}
10
+ s.description = %q{ Howcast offers an Application Programming Interface (API) which allows developers to build applications that interface with Howcast. The Howcast API is RESTful (REpresentational State Transfer) and users of this API will be able: 1) Retreive detailed information about a single video, including metadata such as title, description, video views, rating etc; 2) Retrieve a list of videos restricted by a set of filters offered by Howcast and sorted using several metrics that you can specify (most recent, most views, etc); 3) Search for video; 4) And much more. Note: Before you can use our APIs, you must register an API key, that is submitted with each request.
11
+ }
12
+ s.email = %q{jimmy@howcast.com}
13
+ s.extra_rdoc_files = ["CHANGELOG", "lib/howcast/client/base.rb", "lib/howcast/client/category.rb", "lib/howcast/client/search.rb", "lib/howcast/client/video.rb", "lib/howcast/client.rb", "lib/howcast/errors.rb", "lib/howcast.rb", "README.markdown", "tasks/github.rake"]
14
+ s.files = ["CHANGELOG", "howcast.gemspec", "lib/howcast/client/base.rb", "lib/howcast/client/category.rb", "lib/howcast/client/search.rb", "lib/howcast/client/video.rb", "lib/howcast/client.rb", "lib/howcast/errors.rb", "lib/howcast.rb", "License.txt", "Manifest", "Rakefile", "README.markdown", "spec/howcast/client/base_spec.rb", "spec/howcast/client/category_spec.rb", "spec/howcast/client/search_spec.rb", "spec/howcast/client/video_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/github.rake"]
15
+ s.homepage = %q{http://github.com/howcast/howcast-gem}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Howcast", "--main", "README.markdown"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{howcast}
19
+ s.rubygems_version = %q{1.3.4}
20
+ s.summary = %q{Howcast API Ruby Wrapper}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 3
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ else
28
+ end
29
+ else
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ module Howcast; end
2
+
3
+ def require_local(suffix)
4
+ require(File.expand_path(File.join(File.dirname(__FILE__), suffix)))
5
+ end
6
+
7
+ require_local('howcast/client')
8
+ require_local('howcast/errors')
@@ -0,0 +1,8 @@
1
+ # Used to query the Howcast REST API.
2
+ class Howcast::Client
3
+ end
4
+
5
+ require_local('howcast/client/base.rb')
6
+ require_local('howcast/client/video.rb')
7
+ require_local('howcast/client/search.rb')
8
+ require_local('howcast/client/category.rb')
@@ -0,0 +1,154 @@
1
+ #--
2
+ # Copyright (c) 2008 Howcast Media Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'rubygems'
25
+ require 'hpricot'
26
+ require 'open-uri'
27
+
28
+ class Howcast::Client
29
+ attr_accessor :key
30
+
31
+ # Creates a new howcast client to interact with the Howcast API
32
+ #
33
+ # === Inputs
34
+ #
35
+ # Options include:
36
+ #
37
+ # * <tt>key</tt> -- REQUIRED - API key: obtainable from http://www.howcast.com/api_keys/new
38
+ #
39
+ # === Exceptions
40
+ #
41
+ # * <tt>Howcast::ApiKeyNotFound</tt> -- raised if the options[:key] value is nil
42
+ def initialize(options={})
43
+ raise Howcast::ApiKeyNotFound if options[:key].nil?
44
+ @key = options[:key]
45
+ end
46
+
47
+ protected
48
+ # Establishes a connection with the Howcast API. Will auto append the api key, that was used
49
+ # to instantiate the Client object, to the URL
50
+ #
51
+ # === Inputs
52
+ #
53
+ # * <tt>relative_path</tt> -- should be the path after <tt>http://www.howcast.com/</tt>
54
+ #
55
+ # === Outputs
56
+ #
57
+ # Hpricot object encapsulating the xml returned from the Howcast API
58
+ #
59
+ # === Exceptions
60
+ #
61
+ # * <tt>Howcast::ApiNotFound</tt> -- raised if the requested +relative_path+ is malformed or not available (404)
62
+ # * <tt>Howcast::ApiKeyNotFound</tt> -- raised if the +api_key+ is invalid
63
+ #
64
+ # === Examples
65
+ #
66
+ # Get the Hpricot data for most recent howcast studios videos
67
+ # establish_connection("videos/most_recent/howcast_studios.xml")
68
+ def establish_connection(relative_path)
69
+ h = Hpricot.XML(open(url = attach_api_key("http://www.howcast.com/#{relative_path}")))
70
+ puts "Established connection with: '#{url}'"
71
+ raise Howcast::ApiKeyNotFound if h.at(:error) && h.at(:error).inner_html.match(/Invalid API Key/)
72
+ return h
73
+ rescue URI::InvalidURIError, OpenURI::HTTPError
74
+ raise Howcast::ApiNotFound.new("Invalid URL requested. Refer to the Howcast API for supported URL's")
75
+ end
76
+
77
+ # Parses the xml for a single item from +xml+ and creates a new +klass+ object
78
+ #
79
+ # === Inputs
80
+ #
81
+ # * <tt>xml</tt> -- See below for a sample xml input
82
+ # * <tt>klass</tt> -- Class to create - Video | Category supported
83
+ #
84
+ # Sample input xml
85
+ # <video>
86
+ # <id>1086</id>
87
+ # <rating>96</rating>
88
+ # <title>How To Choose a Paintbrush</title>
89
+ # <category-id>19</category-id>
90
+ # <description>
91
+ # <![CDATA[Yes, you could just use your fingers, but selecting the best brushes for your painting might achieve a slightly more grown-up result.]]>
92
+ # </description>
93
+ # <views>362</views>
94
+ # <permalink>http://www.howcast.com/guides/1086-How-To-Choose-a-Paintbrush</permalink>
95
+ # <username>michaelrsanchez</username>
96
+ # <created-at>Thu, 20 Dec 2007 14:14:58 -0800</created-at>
97
+ # </video>
98
+ #
99
+ # === Outputs
100
+ #
101
+ # +klass+ object with initialized attributes
102
+ def parse_single_xml(xml, klass)
103
+ hash = {}
104
+ klass.attr_accessors.each do |attribute|
105
+ node_name = attribute.to_s.gsub("_", "-") # xml schema uses hyphens for spaces, but ruby uses underscores
106
+ hash[attribute] = !xml.at(node_name).nil? ? xml.at(node_name).inner_text.strip : ""
107
+ end
108
+ hash.values.all?{|v| v==""} ? nil : klass.new(hash)
109
+ end
110
+
111
+ # Creates parameters to append to a uri
112
+ #
113
+ # === Inputs
114
+ #
115
+ # Options are:
116
+ # * <tt>:page</tt> -- the page number
117
+ # * <tt>:use_ampersand</tt> -- boolean to return ampersand (defaults to false)
118
+ #
119
+ # === Outputs
120
+ #
121
+ # A valid suffix string to append to the end of a url
122
+ #
123
+ # === Examples
124
+ #
125
+ # "?page=2"
126
+ # uri_suffix(:page => 2)
127
+ # "&page=2"
128
+ # uri_suffix(:page => 3, :use_ampersand => true)
129
+ def uri_suffix(opts)
130
+ opts && opts[:page] ? "#{opts[:use_ampersand] ? '&' : '?'}page=#{opts[:page]}" : ""
131
+ end
132
+
133
+ # Appends the api key to a url and returns the appended url
134
+ #
135
+ # === Inputs
136
+ #
137
+ # * <tt>url</tt> -- the url to append the api_key to
138
+ #
139
+ # === Outputs
140
+ #
141
+ # The url with the api_key appended
142
+ #
143
+ # === Examples
144
+ #
145
+ # attach_api_key("http://www.howcast.com")
146
+ # => "http://www.howcast.com?api_key=APIKEYHERE"
147
+ #
148
+ # attach_api_key("http://www.howcast.com/videos/most_recent/all?page=2")
149
+ # => "http://www.howcast.com/videos/most_recent/all?page=2&api_key=APIKEYHERE"
150
+ def attach_api_key(url)
151
+ url.match(/\?/) ? "#{url}&api_key=#{self.key}" : "#{url}?api_key=#{self.key}"
152
+ end
153
+ end
154
+
@@ -0,0 +1,85 @@
1
+ #--
2
+ # Copyright (c) 2008 Howcast Media Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ class Howcast::Client
25
+ class Category
26
+ extend WatchAttrAccessors
27
+ attr_accessor :id, :name, :parent_id, :parents
28
+
29
+ # Creates a new Category object which is used to encapsulate all the attributes available
30
+ # from the Howcast Category API. The parents attribute will be an array of category
31
+ # ancestors of the form:
32
+ # [{:id => 1, :name => 'root'}, {:id => 2, :name => 'immediate parent'}]
33
+ #
34
+ # === Inputs
35
+ #
36
+ # * <tt>attributes</tt> -- A hash to set the various attributes of the category object
37
+ #
38
+ # === Examples
39
+ #
40
+ # Initialize a category with name "Arts & Crafts"
41
+ # Category.new :name => "Arts & Crafts"
42
+ def initialize(attributes={})
43
+ attributes.each do |k, v|
44
+ self.send("#{k}=", v) if self.respond_to?(k)
45
+ end
46
+ end
47
+ end
48
+
49
+ # Provides access to the Howcast category API.
50
+ #
51
+ # === Inputs
52
+ #
53
+ # * <tt>id</tt> -- The id of the category to lookup
54
+ #
55
+ # === Outputs
56
+ #
57
+ # Category object if the id exists or nil if the id doesn't exist or is malformed
58
+ #
59
+ # === Exceptions
60
+ #
61
+ # * <tt>Howcast::ApiNotFound</tt>
62
+ #
63
+ # === Examples
64
+ #
65
+ # Get the Howcast category with id 2
66
+ # Howcast::Client.new.category(2)
67
+ def category(id)
68
+ response = establish_connection("categories/#{id}.xml")
69
+ parse_single_category_xml(response.at(:category))
70
+ end
71
+
72
+ private
73
+ # Exception here to parse the <parents> tag in a category, will set a category.parents variable
74
+ # which is an array of parent metadata hases
75
+ # [{:id => '123', :name => "root"}, {:id => "1234", :name => "parent"}]
76
+ def parse_single_category_xml(xml)
77
+ hash = {}
78
+ Category.attr_accessors.each do |attribute|
79
+ node_name = attribute.to_s.gsub("_", "-") # xml schema uses hyphens for spaces, but ruby uses underscores
80
+ hash[attribute] = !xml.at(node_name).nil? ? xml.at(node_name).inner_text.strip : ""
81
+ end
82
+ hash[:parents] = (xml.at('parents')/:category).map{ |c| {:id => c['id'], :name => c.inner_html }}
83
+ hash.values.all?{|v| v==""} ? nil : Category.new(hash)
84
+ end
85
+ end
@@ -0,0 +1,66 @@
1
+ #--
2
+ # Copyright (c) 2008 Howcast Media Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'cgi'
25
+
26
+ class Howcast::Client
27
+ # Provides access to the Howcast video search API.
28
+ #
29
+ # === Inputs
30
+ #
31
+ # * <tt>query</tt> -- The string query which you want to search for
32
+ # The options are:
33
+ # * <tt>:page</tt> -- The page number to retrieve (defaults to 1). There are 10 videos per page.
34
+ # * <tt>:mode</tt> -- Mode to search, using :extended will allow title:something searches
35
+ #
36
+ # === Outputs
37
+ #
38
+ # An array of video objects
39
+ #
40
+ # === Exceptions
41
+ #
42
+ # * <tt>Howcast::ApiNotFound</tt> -- raised if the requested sort and filter is malformed or not available (404)
43
+ # * <tt>ArgumentError</tt> -- raised when the required 1 argument isn't supplied
44
+ #
45
+ # === Examples
46
+ #
47
+ # Get the first page of howcast videos matching 'poker'.
48
+ # Howcast::Client.new.video_search("poker")
49
+ # Get the third page of howcast videos matching 'traveling'
50
+ # Howcast::Client.new.video_search("traveling", :page => 3)
51
+ def search(query, options = {})
52
+ uri = search_url(query, options)
53
+ (establish_connection(uri)/:video).inject([]){ |r, i| r << parse_single_xml(i, Video)}
54
+ end
55
+
56
+ private
57
+ def search_url(query, options={})
58
+ defaults = {:page => nil, :view => nil, :mode => nil}
59
+ options = defaults.update(options)
60
+ query = CGI.escape(query)
61
+ uri = "search.xml?q=#{query}"
62
+ uri += "&view=videos"
63
+ uri += "&mode=extended" if (options[:mode] == :extended)
64
+ uri + uri_suffix(options.merge(:use_ampersand => true))
65
+ end
66
+ end
@@ -0,0 +1,120 @@
1
+ #--
2
+ # Copyright (c) 2008 Howcast Media Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ class Howcast::Client
25
+ module WatchAttrAccessors
26
+ def attr_accessor(*args)
27
+ super(*args)
28
+ @attr_accessors ||= []
29
+ @attr_accessors += args
30
+ end
31
+
32
+ def attr_accessors
33
+ @attr_accessors || []
34
+ end
35
+ end
36
+
37
+ class Video
38
+ extend WatchAttrAccessors
39
+ attr_accessor :id, :title, :permalink, :thumbnail_url, :category_id,
40
+ :views, :username, :duration, :created_at, :rating, :description, :width, :height,
41
+ :badges, :easy_steps, :embed
42
+ # Creates a new Video object which is used to encapsulate all the attributes available
43
+ # from the Howcast Video API
44
+ #
45
+ # === Inputs
46
+ #
47
+ # * <tt>attributes</tt> -- A hash to set the various attributes of the video object
48
+ #
49
+ # === Examples
50
+ #
51
+ # Initialize a video with title "hello" and rating 20
52
+ # Video.new :title => "hello", :rating => 20
53
+ def initialize(attributes={})
54
+ attributes.each do |k, v|
55
+ self.send("#{k}=", v) if self.respond_to?(k)
56
+ end
57
+ end
58
+
59
+ # Return true if the video contains easy step by step directions, else false
60
+ def easy_steps?
61
+ easy_steps == "true"
62
+ end
63
+ end
64
+
65
+ # Provides access to the Howcast video API.
66
+ #
67
+ # === Inputs
68
+ #
69
+ # * <tt>id</tt> -- The id of the video to lookup
70
+ #
71
+ # === Outputs
72
+ #
73
+ # Video object if the id exists or nil if the id doesn't exist or is malformed
74
+ #
75
+ # === Exceptions
76
+ #
77
+ # * <tt>Howcast::ApiNotFound</tt>
78
+ #
79
+ # === Examples
80
+ #
81
+ # Get the Howcast video with id 2
82
+ # Howcast::Client.new.video(2)
83
+ def video(id)
84
+ response = establish_connection("videos/#{id}.xml")
85
+ parse_single_xml(response.at(:video), Video)
86
+ end
87
+
88
+ # Provides access to the Howcast list videos API.
89
+ #
90
+ # === Inputs
91
+ #
92
+ # The options are:
93
+ # * <tt>:page</tt> -- The page number to retrieve (defaults to 1). There are 20 videos per page.
94
+ # * <tt>:sort</tt> -- One of +most_recent+ (default) | +most_viewed+ | +top_rated+
95
+ # * <tt>:filter</tt> -- One of +all+ | +howcast_studios+ (default)
96
+ #
97
+ # === Outputs
98
+ # An array of video objects
99
+ #
100
+ # === Exceptions
101
+ # * <tt>Howcast::ApiNotFound</tt> -- raised if the requested sort and filter is malformed or not available (404)
102
+ #
103
+ # === Examples
104
+ #
105
+ # Get the first page of most recent howcast studios videos.
106
+ # Howcast::Client.new.videos
107
+ # Get the third page of top favorites which are featured
108
+ # Howcast::Client.new.videos(:page => 3, :sort => "top_favorites", :filter => "top_rated")
109
+ def videos(options = {})
110
+ uri = videos_url(options)
111
+ (establish_connection(uri)/:video).inject([]){ |r, i| r << parse_single_xml(i, Video)}
112
+ end
113
+
114
+ private
115
+ def videos_url(options={})
116
+ defaults = {:page => nil, :sort => "most_recent", :filter => "howcast_studios", :category_id => nil}
117
+ options = defaults.update(options)
118
+ "videos/#{options[:sort]}/#{options[:filter]}#{"/#{options[:category_id]}" if options[:category_id]}#{"/#{options[:page]}" if options[:page]}.xml"
119
+ end
120
+ end
@@ -0,0 +1,8 @@
1
+
2
+ class Howcast::ApiNotFound < Exception
3
+
4
+ end
5
+
6
+ class Howcast::ApiKeyNotFound < Exception
7
+
8
+ end
@@ -0,0 +1,14 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Howcast::Client, "initialize" do
4
+ it "should create a client object with all the inputted attributes" do
5
+ client = Howcast::Client.new :key => "myapikey"
6
+ client.should be_instance_of(Howcast::Client)
7
+ end
8
+
9
+ it "should raise a Howcast::ApiKeyNotFound error when no key is supplied" do
10
+ lambda {
11
+ Howcast::Client.new
12
+ }.should raise_error(Howcast::ApiKeyNotFound)
13
+ end
14
+ end
@@ -0,0 +1,45 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Howcast::Client::Category, "initialize" do
4
+ it "should create a category object with all the inputted attributes" do
5
+ category = Howcast::Client::Category.new :name => "category_title"
6
+ category.name.should == 'category_title'
7
+ end
8
+
9
+ it "should ignore inputs which are not category attributes" do
10
+ category = Howcast::Client::Category.new :not_an_attribute => "value", :name => "category_title"
11
+ category.respond_to?(:not_an_attribute).should be_false
12
+ category.name.should == "category_title"
13
+ end
14
+ end
15
+
16
+ describe Howcast::Client, "category" do
17
+ before do
18
+ @hc = Howcast::Client.new(:key => "myapikey")
19
+ @hc.stub!(:open).and_return(category_xml)
20
+ end
21
+
22
+ it "should establish a connection with the correct category url" do
23
+ @hc.should_receive(:open).with("http://www.howcast.com/categories/2.xml?api_key=myapikey").and_return(category_xml)
24
+ @hc.category(2)
25
+ end
26
+
27
+ it "should raise Howcast::ApiKeyNotFound error when the response contains Invalid API Key" do
28
+ lambda {
29
+ @hc.stub!(:open).and_return(invalid_api_key_xml)
30
+ @hc.category(2)
31
+ }.should raise_error(Howcast::ApiKeyNotFound)
32
+ end
33
+
34
+ it "should set the title attribute in the category model response" do
35
+ @hc.category(2).name.should == "General African Travel"
36
+ end
37
+
38
+ it "should set the parent_id attribute in the category model response" do
39
+ @hc.category(2).parent_id.should == "1584"
40
+ end
41
+
42
+ it "should set the parents metadata hash" do
43
+ @hc.category(2).parents.should == [{:id => "1571", :name => "Travel"}, {:id => "1584", :name => "African Travel"}]
44
+ end
45
+ end
@@ -0,0 +1,47 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Howcast::Client, "search" do
4
+ before do
5
+ @hc = Howcast::Client.new(:key => "myapikey")
6
+ end
7
+
8
+ it "should establish a connection with search.xml?q=something&view=videos when query is 'something'" do
9
+ @hc.should_receive(:open).with("http://www.howcast.com/search.xml?q=something&view=videos&api_key=myapikey").and_return(videos_xml)
10
+ @hc.search("something")
11
+ end
12
+
13
+ it "should establish a connection with search.xml?q=something&view=videos&page=2 when query is 'something' and :page => 2" do
14
+ @hc.should_receive(:open).with("http://www.howcast.com/search.xml?q=something&view=videos&page=2&api_key=myapikey").and_return(videos_xml)
15
+ @hc.search("something", :page => 2)
16
+ end
17
+
18
+ it "should escape the query when esablishing the connection" do
19
+ @hc.should_receive(:open).with("http://www.howcast.com/search.xml?q=something+%26+something&view=videos&api_key=myapikey").and_return(videos_xml)
20
+ @hc.search("something & something")
21
+ end
22
+
23
+ it "should append mode=extended when passed in as an option" do
24
+ @hc.should_receive(:open).with("http://www.howcast.com/search.xml?q=something&view=videos&mode=extended&api_key=myapikey").and_return(videos_xml)
25
+ @hc.search("something", :mode => :extended)
26
+ end
27
+
28
+ it "should raise a Howcast::ApiNotFound if url is invalid" do
29
+ lambda {
30
+ @hc.should_receive(:open).and_raise(URI::InvalidURIError)
31
+ @hc.search("something")
32
+ }.should raise_error(Howcast::ApiNotFound)
33
+ end
34
+
35
+ it "should return an empty array if there are no results" do
36
+ @hc.should_receive(:open).and_return(blank_videos_xml)
37
+ @hc.search("something").should be_empty
38
+ end
39
+
40
+ it "should return an array of video objects if there are some returned" do
41
+ @hc.should_receive(:open).and_return(videos_xml)
42
+ videos = @hc.search("something")
43
+ videos.size.should == 3
44
+ videos.first.permalink.should == "http://www.howcast.com/videos/1101-How-To-Pretend-Youre-a-Real-New-Yorker"
45
+ videos.last.permalink.should == "http://www.howcast.com/videos/866-How-To-Make-a-Water-Gun-Alarm-Clock"
46
+ end
47
+ end
@@ -0,0 +1,121 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Howcast::Client::Video, "initialize" do
4
+ it "should create a video object with all the inputted attributes" do
5
+ video = Howcast::Client::Video.new :title => "video_title"
6
+ video.title.should == 'video_title'
7
+ end
8
+
9
+ it "should ignore inputs which are not video attributes" do
10
+ video = Howcast::Client::Video.new :not_an_attribute => "value", :title => "video_title"
11
+ video.respond_to?(:not_an_attribute).should be_false
12
+ video.title.should == "video_title"
13
+ end
14
+ end
15
+
16
+ describe Howcast::Client, "video" do
17
+ before do
18
+ @hc = Howcast::Client.new(:key => "myapikey")
19
+ @hc.stub!(:open).and_return(video_xml)
20
+ end
21
+
22
+ it "should establish a connection with the correct video url" do
23
+ @hc.should_receive(:open).with("http://www.howcast.com/videos/2.xml?api_key=myapikey").and_return(video_xml)
24
+ @hc.video(2)
25
+ end
26
+
27
+ it "should raise Howcast::ApiKeyNotFound error when the response contains Invalid API Key" do
28
+ lambda {
29
+ @hc.stub!(:open).and_return(invalid_api_key_xml)
30
+ @hc.video(2)
31
+ }.should raise_error(Howcast::ApiKeyNotFound)
32
+ end
33
+
34
+ it "should return nil whenever the result xml is empty" do
35
+ @hc.stub!(:open).and_return(blank_video_xml)
36
+ @hc.video(2).should be_nil
37
+ end
38
+
39
+ it "should set the embed attribute in the video model response" do
40
+ @hc.video(2).embed.should == %(<object width="425" height="352" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id="howcastplayer"><param name="movie" value="http://www.howcast.com/flash/howcast_player.swf?file=233"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param><embed src="http://www.howcast.com/flash/howcast_player.swf?file=233" type="application/x-shockwave-flash" width="425" height="352" allowFullScreen="true" allowScriptAccess="always" ></embed></object>)
41
+ end
42
+
43
+ it "should set the title attribute in the video model response" do
44
+ @hc.video(2).title.should == "How To Do a Noble Pose"
45
+ end
46
+
47
+ it "should set the views attribute in the video model response" do
48
+ @hc.video(2).views.should == "38"
49
+ end
50
+
51
+ it "should set the badges in the video model response" do
52
+ @hc.video(2).badges.should == "Howcast Studios"
53
+ end
54
+
55
+ it "should set the easy_steps boolean in the video model response" do
56
+ @hc.video(2).easy_steps?.should be_true
57
+ end
58
+
59
+ it "should set the rating attribute in the video model response" do
60
+ @hc.video(2).rating.should == "2.0"
61
+ end
62
+
63
+ it "should set the description attribute in the video model response" do
64
+ @hc.video(2).description.should == "You recognize the Noble Pose as the dreaded \"sit-and-reach\" from your childhood gym class. A sample <a href=\"howcast.com\">link</a>"
65
+ end
66
+
67
+ it "should set the permalink attribute in the video model response" do
68
+ @hc.video(2).permalink.should == "http://www.howcast.com/videos/233-How-To-Do-a-Noble-Pose"
69
+ end
70
+ end
71
+
72
+ describe Howcast::Client, "videos" do
73
+ before do
74
+ @hc = Howcast::Client.new(:key => "myapikey")
75
+ end
76
+
77
+ it "should establish a connection with videos/most_recent/howcast_studios.xml by default" do
78
+ @hc.should_receive(:open).with("http://www.howcast.com/videos/most_recent/howcast_studios.xml?api_key=myapikey").and_return(videos_xml)
79
+ @hc.videos
80
+ end
81
+
82
+ it "should establish a connection with videos/most_recent/howcast_studios/2.xml when :page => 2" do
83
+ @hc.should_receive(:open).with("http://www.howcast.com/videos/most_recent/howcast_studios/2.xml?api_key=myapikey").and_return(videos_xml)
84
+ @hc.videos(:page => 2)
85
+ end
86
+
87
+ it "should establish a connection with videos/most_viewed/howcast_studios.xml when :sort => most_viewed" do
88
+ @hc.should_receive(:open).with("http://www.howcast.com/videos/most_viewed/howcast_studios.xml?api_key=myapikey").and_return(videos_xml)
89
+ @hc.videos(:sort => "most_viewed")
90
+ end
91
+
92
+ it "should establish a connection with videos/most_viewed/directors_program.xml when :sort => most_viewed and :filter => directors_program" do
93
+ @hc.should_receive(:open).with("http://www.howcast.com/videos/most_viewed/directors_program.xml?api_key=myapikey").and_return(videos_xml)
94
+ @hc.videos(:sort => "most_viewed", :filter => "directors_program")
95
+ end
96
+
97
+ it "should establish a connection with videos/top_rated/directors_program.xml when :sort => most_viewed and :filter => directors_program" do
98
+ @hc.should_receive(:open).with("http://www.howcast.com/videos/top_rated/directors_program.xml?api_key=myapikey").and_return(videos_xml)
99
+ @hc.videos(:sort => "top_rated", :filter => "directors_program")
100
+ end
101
+
102
+ it "should raise a Howcast::ApiNotFound if url is invalid" do
103
+ lambda {
104
+ @hc.should_receive(:open).and_raise(URI::InvalidURIError)
105
+ @hc.videos
106
+ }.should raise_error(Howcast::ApiNotFound)
107
+ end
108
+
109
+ it "should return an empty array if there are no results" do
110
+ @hc.should_receive(:open).and_return(blank_videos_xml)
111
+ @hc.videos.should be_empty
112
+ end
113
+
114
+ it "should return an array of video objects if there are some returned" do
115
+ @hc.should_receive(:open).and_return(videos_xml)
116
+ videos = @hc.videos
117
+ videos.size.should == 3
118
+ videos.first.permalink.should == "http://www.howcast.com/videos/1101-How-To-Pretend-Youre-a-Real-New-Yorker"
119
+ videos.last.permalink.should == "http://www.howcast.com/videos/866-How-To-Make-a-Water-Gun-Alarm-Clock"
120
+ end
121
+ end
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,207 @@
1
+ ENV["RAILS_ENV"] ||= "test"
2
+ begin
3
+ require 'spec'
4
+ rescue LoadError
5
+ require 'rubygems'
6
+ require 'spec'
7
+ end
8
+
9
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/howcast")
10
+
11
+ Spec::Runner.configure do |config|
12
+ def invalid_api_key_xml
13
+ <<-INVALID
14
+ <?xml version="1.0" encoding="UTF-8"?>
15
+ <howcast version="0.1">
16
+ <error>Invalid API Key</error>
17
+ </howcast>
18
+ INVALID
19
+ end
20
+
21
+ def blank_video_xml
22
+ <<-VID
23
+ <?xml version="1.0" encoding="UTF-8"?>
24
+ <howcast version="0.1">
25
+ <video>
26
+ </video>
27
+ </howcast>
28
+ VID
29
+ end
30
+
31
+ def blank_guides_xml
32
+ <<-GUID
33
+ <?xml version="1.0" encoding="UTF-8"?>
34
+ <howcast version="0.1">
35
+ <guides>
36
+ </guides>
37
+ </howcast>
38
+ GUID
39
+ end
40
+
41
+ def blank_videos_xml
42
+ <<-VID
43
+ <?xml version="1.0" encoding="UTF-8"?>
44
+ <howcast version="0.1">
45
+ <videos>
46
+ </videos>
47
+ </howcast>
48
+ VID
49
+ end
50
+
51
+ def category_xml
52
+ <<-CAT
53
+ <?xml version="1.0" encoding="UTF-8"?>
54
+ <howcast version="0.1">
55
+ <category>
56
+ <id>1255</id>
57
+ <name>General African Travel</name>
58
+ <parent-id>1584</parent-id>
59
+ <parents>
60
+ <category id="1571">Travel</category>
61
+ <category parent_id="1571" id="1584">African Travel</category>
62
+ </parents>
63
+ <subcategories>
64
+ <category>
65
+ <id>1265</id>
66
+ <name>African Travel</name>
67
+ </category>
68
+ <category>
69
+ <id>1289</id>
70
+ <name>European Travel</name>
71
+ </category>
72
+ <category>
73
+ <id>1256</id>
74
+ <name>General Travel</name>
75
+ </category>
76
+ <category>
77
+ <id>1311</id>
78
+ <name>General U.S. Travel</name>
79
+ </category>
80
+ </subcategories>
81
+ </category>
82
+ </howcast>
83
+ CAT
84
+ end
85
+
86
+ def video_xml
87
+ <<-VID
88
+ <?xml version="1.0" encoding="UTF-8"?>
89
+ <howcast version="0.1">
90
+ <video>
91
+ <category-id>2</category-id>
92
+ <duration>22</duration>
93
+ <id>233</id>
94
+ <easy-steps>true</easy-steps>
95
+ <badges>Howcast Studios</badges>
96
+ <title>How To Do a Noble Pose</title>
97
+ <embed>
98
+ <![CDATA[<object width="425" height="352" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id="howcastplayer"><param name="movie" value="http://www.howcast.com/flash/howcast_player.swf?file=233"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param><embed src="http://www.howcast.com/flash/howcast_player.swf?file=233" type="application/x-shockwave-flash" width="425" height="352" allowFullScreen="true" allowScriptAccess="always" ></embed></object>]]>
99
+ </embed>
100
+ <description>
101
+ <![CDATA[You recognize the Noble Pose as the dreaded "sit-and-reach" from your childhood gym class. A sample <a href="howcast.com">link</a>]]>
102
+ </description>
103
+ <permalink>http://www.howcast.com/videos/233-How-To-Do-a-Noble-Pose</permalink>
104
+ <username>joekulak</username>
105
+ <thumbnail-url>http://www.howcast.com/system/thumbnails/233/Mind.How_to_Do_the_Noble_Pose_SD_xxlarge.jpg</thumbnail-url>
106
+ <views>38</views>
107
+ <rating>2.0</rating>
108
+ <created-at>#{Time.now.rfc822}</created-at>
109
+ </video>
110
+ </howcast>
111
+ VID
112
+ end
113
+
114
+ def guides_xml
115
+ <<-VID
116
+ <?xml version="1.0" encoding="UTF-8"?>
117
+ <howcast version="0.1">
118
+ <title>Howcast - Top Favorites Howcast Written Guides </title>
119
+ <guides>
120
+ <guide>
121
+ <rating>94</rating>
122
+ <title>How To Pretend You&#8217;re a Real New Yorker</title>
123
+ <permalink>http://www.howcast.com/guides/1101-How-To-Pretend-Youre-a-Real-New-Yorker</permalink>
124
+ <id>1101</id>
125
+ <category-id>1642</category-id>
126
+ <description>There&#8217;s nothing wrong with being a tourist in New York City. But if you want to blend in, here&#8217;s what you need to know.</description>
127
+ <views>2739</views>
128
+ <username>vinzfeller</username>
129
+ <created-at>2008-02-05T19:41:21-08:00</created-at>
130
+ </guide>
131
+ <guide>
132
+ <rating>96</rating>
133
+ <title>How To Look Great in Photographs</title>
134
+ <permalink>http://www.howcast.com/guides/577-How-To-Look-Great-in-Photographs</permalink>
135
+ <id>577</id>
136
+ <category-id>22</category-id>
137
+ <description>Sure, a beautiful photograph takes some skill behind the lens, but it takes a little skill in front of it, too.</description>
138
+ <views>3831</views>
139
+ <username>nicholas</username>
140
+ <created-at>2008-01-12T09:33:06-08:00</created-at>
141
+ </guide>
142
+ <guide>
143
+ <rating>98</rating>
144
+ <title>How To Make a Water Gun Alarm Clock</title>
145
+ <permalink>http://www.howcast.com/guides/866-How-To-Make-a-Water-Gun-Alarm-Clock</permalink>
146
+ <id>866</id>
147
+ <category-id>1728</category-id>
148
+ <description>Having a little trouble waking up in the morning? There&#8217;s nothing like a good soaking to get you going.</description>
149
+ <views>2663</views>
150
+ <username>Howcast</username>
151
+ <created-at>2008-01-31T20:47:08-08:00</created-at>
152
+ </guide>
153
+ </guides>
154
+ </howcast>
155
+ VID
156
+ end
157
+
158
+ def videos_xml
159
+ <<-VID
160
+ <?xml version="1.0" encoding="UTF-8"?>
161
+ <howcast version="0.1">
162
+ <title>Howcast - Top Favorites Howcast Studios Videos </title>
163
+ <videos>
164
+ <video>
165
+ <rating>94</rating>
166
+ <title>How To Pretend You&#8217;re a Real New Yorker</title>
167
+ <permalink>http://www.howcast.com/videos/1101-How-To-Pretend-Youre-a-Real-New-Yorker</permalink>
168
+ <id>1101</id>
169
+ <category-id>1642</category-id>
170
+ <description>There&#8217;s nothing wrong with being a tourist in New York City. But if you want to blend in, here&#8217;s what you need to know.</description>
171
+ <thumbnail-url>http://www.howcast.com/system/thumbnails/1101/ppn_feller_real_newyorker_sd_medium.jpg</thumbnail-url>
172
+ <views>2739</views>
173
+ <username>vinzfeller</username>
174
+ <duration>203</duration>
175
+ <created-at>2008-02-05T19:41:21-08:00</created-at>
176
+ </video>
177
+ <video>
178
+ <rating>96</rating>
179
+ <title>How To Look Great in Photographs</title>
180
+ <permalink>http://www.howcast.com/videos/577-How-To-Look-Great-in-Photographs</permalink>
181
+ <id>577</id>
182
+ <category-id>22</category-id>
183
+ <description>Sure, a beautiful photograph takes some skill behind the lens, but it takes a little skill in front of it, too.</description>
184
+ <thumbnail-url>http://www.howcast.com/system/thumbnails/577/Arts.How_to_Look_Great_in_Photographs_SD_medium.jpg</thumbnail-url>
185
+ <views>3831</views>
186
+ <username>nicholas</username>
187
+ <duration>156</duration>
188
+ <created-at>2008-01-12T09:33:06-08:00</created-at>
189
+ </video>
190
+ <video>
191
+ <rating>98</rating>
192
+ <title>How To Make a Water Gun Alarm Clock</title>
193
+ <permalink>http://www.howcast.com/videos/866-How-To-Make-a-Water-Gun-Alarm-Clock</permalink>
194
+ <id>866</id>
195
+ <category-id>1728</category-id>
196
+ <description>Having a little trouble waking up in the morning? There&#8217;s nothing like a good soaking to get you going.</description>
197
+ <thumbnail-url>http://www.howcast.com/system/thumbnails/866/watergunthumb2_medium.jpg</thumbnail-url>
198
+ <views>2663</views>
199
+ <username>Howcast</username>
200
+ <duration>108</duration>
201
+ <created-at>2008-01-31T20:47:08-08:00</created-at>
202
+ </video>
203
+ </videos>
204
+ </howcast>
205
+ VID
206
+ end
207
+ end
@@ -0,0 +1,3 @@
1
+ task :build_for_github => [:manifest, :build_gemspec] do
2
+ p `ruby script/github-test.rb howcast.gemspec`
3
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: howcast-howcast
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.7
5
+ platform: ruby
6
+ authors:
7
+ - Jingshen Jimmy Zhang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-19 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: "Howcast offers an Application Programming Interface (API) which allows developers to build applications that interface with Howcast. The Howcast API is RESTful (REpresentational State Transfer) and users of this API will be able: 1) Retreive detailed information about a single video, including metadata such as title, description, video views, rating etc; 2) Retrieve a list of videos restricted by a set of filters offered by Howcast and sorted using several metrics that you can specify (most recent, most views, etc); 3) Search for video; 4) And much more. Note: Before you can use our APIs, you must register an API key, that is submitted with each request."
17
+ email: jimmy@howcast.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - CHANGELOG
24
+ - lib/howcast/client/base.rb
25
+ - lib/howcast/client/category.rb
26
+ - lib/howcast/client/search.rb
27
+ - lib/howcast/client/video.rb
28
+ - lib/howcast/client.rb
29
+ - lib/howcast/errors.rb
30
+ - lib/howcast.rb
31
+ - README.markdown
32
+ - tasks/github.rake
33
+ files:
34
+ - CHANGELOG
35
+ - howcast.gemspec
36
+ - lib/howcast/client/base.rb
37
+ - lib/howcast/client/category.rb
38
+ - lib/howcast/client/search.rb
39
+ - lib/howcast/client/video.rb
40
+ - lib/howcast/client.rb
41
+ - lib/howcast/errors.rb
42
+ - lib/howcast.rb
43
+ - License.txt
44
+ - Manifest
45
+ - Rakefile
46
+ - README.markdown
47
+ - spec/howcast/client/base_spec.rb
48
+ - spec/howcast/client/category_spec.rb
49
+ - spec/howcast/client/search_spec.rb
50
+ - spec/howcast/client/video_spec.rb
51
+ - spec/spec.opts
52
+ - spec/spec_helper.rb
53
+ - tasks/github.rake
54
+ has_rdoc: false
55
+ homepage: http://github.com/howcast/howcast-gem
56
+ licenses:
57
+ post_install_message:
58
+ rdoc_options:
59
+ - --line-numbers
60
+ - --inline-source
61
+ - --title
62
+ - Howcast
63
+ - --main
64
+ - README.markdown
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "1.2"
78
+ version:
79
+ requirements: []
80
+
81
+ rubyforge_project: howcast
82
+ rubygems_version: 1.3.5
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: Howcast API Ruby Wrapper
86
+ test_files: []
87
+