howcast 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History.txt ADDED
@@ -0,0 +1,19 @@
1
+ == 0.3.1 2008-04-08
2
+
3
+ * Ability to search in extended mode
4
+
5
+ == 0.3 2008-03-27
6
+
7
+ * API Key support
8
+
9
+ == 0.2 2008-03-11
10
+
11
+ * Support for WikiGuide API - list and search
12
+
13
+ == 0.1.1 2008-03-04
14
+
15
+ * Minor bug fixes and documentation
16
+
17
+ == 0.1.0 2008-02-19
18
+
19
+ * 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.txt ADDED
@@ -0,0 +1,35 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ config/hoe.rb
7
+ config/requirements.rb
8
+ lib/howcast.rb
9
+ lib/howcast/client.rb
10
+ lib/howcast/version.rb
11
+ lib/howcast/errors.rb
12
+ lib/howcast/client/base.rb
13
+ lib/howcast/client/video.rb
14
+ lib/howcast/client/guide.rb
15
+ lib/howcast/client/search.rb
16
+ log/debug.log
17
+ script/destroy
18
+ script/generate
19
+ script/txt2html
20
+ setup.rb
21
+ spec/spec.opts
22
+ spec/spec_helper.rb
23
+ spec/howcast/client/base_spec.rb
24
+ spec/howcast/client/guide_spec.rb
25
+ spec/howcast/client/video_spec.rb
26
+ spec/howcast/client/search_spec.rb
27
+ tasks/deployment.rake
28
+ tasks/environment.rake
29
+ tasks/rspec.rake
30
+ tasks/website.rake
31
+ website/index.html
32
+ website/index.txt
33
+ website/javascripts/rounded_corners_lite.inc.js
34
+ website/stylesheets/screen.css
35
+ website/template.rhtml
data/README.txt ADDED
@@ -0,0 +1,30 @@
1
+ Howcast API Ruby Wrapper
2
+ Copyright (c) 2008 Howcast Media Inc.
3
+
4
+ == Example
5
+
6
+ An example ruby script to get some howcast videos
7
+
8
+ require 'rubygems'
9
+ require 'howcast'
10
+
11
+ hc = Howcast::Client.new(:key => "INSERT API KEY HERE")
12
+ # Will print out the video titles of the first page of recent howcast studios videos
13
+ puts "Recent Howcast Studios Videos"
14
+ hc.videos.each do |v|
15
+ puts v.title
16
+ end
17
+
18
+ puts "2nd Page of Recent Featured Videos"
19
+ # Will print out the video titles of the 2nd page of recent featured videos
20
+ hc.videos(:page => 2, :sort => "most_recent", :filter => "featured").each do |v|
21
+ puts v.title
22
+ end
23
+
24
+ puts "Videos matching 'origami'"
25
+ hc.video_search("origami").each do |v|
26
+ puts v.title
27
+ end
28
+
29
+ puts "Video with id 946"
30
+ puts hc.video(946).title
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
data/config/hoe.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'howcast/version'
2
+
3
+ AUTHOR = 'Michael Murray' # can also be an array of Authors
4
+ EMAIL = "michael+gem@howcast.com"
5
+ DESCRIPTION = "Howcast API Ruby Wrapper"
6
+ GEM_NAME = 'howcast' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'howcast' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+
11
+ @config_file = "~/.rubyforge/user-config.yml"
12
+ @config = nil
13
+ RUBYFORGE_USERNAME = "mdmurray"
14
+ def rubyforge_username
15
+ unless @config
16
+ begin
17
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
18
+ rescue
19
+ puts <<-EOS
20
+ ERROR: No rubyforge config file found: #{@config_file}
21
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
22
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
23
+ EOS
24
+ exit
25
+ end
26
+ end
27
+ RUBYFORGE_USERNAME.replace @config["username"]
28
+ end
29
+
30
+
31
+ REV = nil
32
+ # UNCOMMENT IF REQUIRED:
33
+ # REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
34
+ VERS = Howcast::Version::STRING + (REV ? ".#{REV}" : "")
35
+ RDOC_OPTS = ['--quiet', '--title', 'howcast documentation',
36
+ "--opname", "index.html",
37
+ "--line-numbers",
38
+ "--main", "README",
39
+ "--inline-source"]
40
+
41
+ class Hoe
42
+ def extra_deps
43
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
44
+ @extra_deps
45
+ end
46
+ end
47
+
48
+ # Generate all the Rake tasks
49
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
50
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
51
+ p.developer(AUTHOR, EMAIL)
52
+ p.description = DESCRIPTION
53
+ p.summary = DESCRIPTION
54
+ p.url = HOMEPATH
55
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
56
+ p.test_globs = ["test/**/test_*.rb"]
57
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
58
+
59
+ # == Optional
60
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
61
+ p.extra_deps = [['hpricot', '>= 0.6']] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
62
+
63
+ # p.spec_extras = {} # A hash of extra values to set in the gemspec.
64
+
65
+ end
66
+
67
+ CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
68
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
69
+ hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
70
+ hoe.rsync_args = '-av --delete --ignore-errors'
@@ -0,0 +1,17 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
16
+
17
+ require 'howcast'
data/lib/howcast.rb ADDED
@@ -0,0 +1,9 @@
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/version')
8
+ require_local('howcast/client')
9
+ 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/guide.rb')
8
+ require_local('howcast/client/search.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 - Guide | Video supported
83
+ #
84
+ # Sample input xml
85
+ # <guide>
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
+ # </guide>
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,89 @@
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 Guide
26
+ extend WatchAttrAccessors
27
+ attr_accessor :id, :title, :permalink, :category_id,
28
+ :views, :username, :created_at, :rating, :description
29
+ # Creates a new Guide object which is used to encapsulate all the attributes available
30
+ # from the Howcast Guide API
31
+ #
32
+ # === Inputs
33
+ #
34
+ # * <tt>attributes</tt> -- A hash to set the various attributes of the guide object
35
+ #
36
+ # === Examples
37
+ #
38
+ # Initialize a guide with title "hello" and rating 20
39
+ # Guide.new :title => "hello", :rating => 20
40
+ def initialize(attributes={})
41
+ attributes.each do |k, v|
42
+ self.send("#{k}=", v) if self.respond_to?(k)
43
+ end
44
+ end
45
+ end
46
+
47
+ # REFACTOR: Consider combining with videos method
48
+ # Provides access to the Howcast list guides API.
49
+ #
50
+ # === Inputs
51
+ #
52
+ # The options are:
53
+ # * <tt>:page</tt> -- The page number to retrieve (defaults to 1). There are 16 guides per page.
54
+ # * <tt>:sort</tt> -- One of +most_recent+ (default) | +most_viewed+ | +top_rated+ | +most_emailed+ | +most_discussed+ | +top_favorites+
55
+ # * <tt>:filter</tt> -- One of +all+ | +featured+ | +howcast_written+ (default) | +community_written+
56
+ #
57
+ # === Outputs
58
+ # An array of guide objects
59
+ #
60
+ # === Exceptions
61
+ # * <tt>Howcast::ApiNotFound</tt> -- raised if the requested sort and filter is malformed or not available (404)
62
+ #
63
+ # === Examples
64
+ #
65
+ # Get the first page of most recent howcast written guides.
66
+ # Howcast::Client.new.guides
67
+ # Get the third page of top favorites which are featured
68
+ # Howcast::Client.new.guides(:page => 3, :sort => "top_favorites", :filter => "featured")
69
+ def guides(options = {})
70
+ uri = guides_url(options)
71
+ collection = []
72
+ (establish_connection(uri)/:guide).each do |item|
73
+ collection << parse_single_xml(item, Guide)
74
+ end
75
+ collection
76
+ end
77
+
78
+ private
79
+ def guides_url(options={})
80
+ defaults = {:page => nil, :sort => "most_recent", :filter => "howcast_written", :category_id => nil}
81
+ options = defaults.update(options)
82
+ if options[:category_id].nil?
83
+ uri = "guides/#{options[:sort]}/#{options[:filter]}.xml"
84
+ else
85
+ uri = "guides/#{options[:sort]}/#{options[:filter]}/#{options[:category_id]}.xml"
86
+ end
87
+ uri + uri_suffix(options)
88
+ end
89
+ end