howcast-howcast 0.4.7
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +60 -0
- data/License.txt +20 -0
- data/Manifest +20 -0
- data/README.markdown +46 -0
- data/Rakefile +17 -0
- data/howcast.gemspec +31 -0
- data/lib/howcast.rb +8 -0
- data/lib/howcast/client.rb +8 -0
- data/lib/howcast/client/base.rb +154 -0
- data/lib/howcast/client/category.rb +85 -0
- data/lib/howcast/client/search.rb +66 -0
- data/lib/howcast/client/video.rb +120 -0
- data/lib/howcast/errors.rb +8 -0
- data/spec/howcast/client/base_spec.rb +14 -0
- data/spec/howcast/client/category_spec.rb +45 -0
- data/spec/howcast/client/search_spec.rb +47 -0
- data/spec/howcast/client/video_spec.rb +121 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +207 -0
- data/tasks/github.rake +3 -0
- metadata +87 -0
data/CHANGELOG
ADDED
@@ -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
|
data/License.txt
ADDED
@@ -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.
|
data/Manifest
ADDED
@@ -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
|
data/README.markdown
ADDED
@@ -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(" -> ")}"
|
data/Rakefile
ADDED
@@ -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 }
|
data/howcast.gemspec
ADDED
@@ -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
|
data/lib/howcast.rb
ADDED
@@ -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,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
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
@@ -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’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’s nothing wrong with being a tourist in New York City. But if you want to blend in, here’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’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’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’s nothing wrong with being a tourist in New York City. But if you want to blend in, here’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’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
|
data/tasks/github.rake
ADDED
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
|
+
|