howcast 0.7.4 → 0.7.15

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.
Files changed (47) hide show
  1. data/.gitignore +6 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +38 -0
  6. data/{README.markdown → README.md} +44 -13
  7. data/Rakefile +6 -39
  8. data/VERSION +1 -1
  9. data/fixtures/categories.xml +2230 -0
  10. data/fixtures/category.1585.xml +24 -0
  11. data/fixtures/homepage.staff_videos.xml +315 -0
  12. data/fixtures/invalid.api_key.xml +6 -0
  13. data/fixtures/playlist.4566.xml +484 -0
  14. data/fixtures/users.someone.profile.videos.xml +440 -0
  15. data/fixtures/video.233.generated.xml +1563 -0
  16. data/fixtures/video.233.xml +830 -0
  17. data/howcast.gemspec +31 -90
  18. data/lib/howcast.rb +1 -1
  19. data/lib/howcast/client.rb +1 -1
  20. data/lib/howcast/client/base.rb +232 -188
  21. data/lib/howcast/client/category.rb +31 -13
  22. data/lib/howcast/client/homepage.rb +10 -8
  23. data/lib/howcast/client/marker.rb +2 -0
  24. data/lib/howcast/client/playlist.rb +2 -0
  25. data/lib/howcast/client/search.rb +11 -11
  26. data/lib/howcast/client/type.rb +48 -0
  27. data/lib/howcast/client/user.rb +2 -0
  28. data/lib/howcast/client/utils.rb +53 -0
  29. data/lib/howcast/client/video.rb +39 -25
  30. data/lib/howcast/ext/string.rb +8 -0
  31. data/lib/howcast/hpricot/elements.rb +22 -0
  32. data/lib/howcast/version.rb +3 -0
  33. data/script/github-test.rb +24 -0
  34. data/spec/howcast/client/base_spec.rb +2 -2
  35. data/spec/howcast/client/category_spec.rb +41 -2
  36. data/spec/howcast/client/homepage_spec.rb +8 -8
  37. data/spec/howcast/client/playlist_spec.rb +6 -4
  38. data/spec/howcast/client/search_spec.rb +7 -8
  39. data/spec/howcast/client/user_spec.rb +9 -7
  40. data/spec/howcast/client/video_spec.rb +106 -22
  41. data/spec/spec_helper.rb +4 -7
  42. data/spec/xml_fixtures_helper.rb +20 -2895
  43. metadata +94 -28
  44. data/CHANGELOG +0 -108
  45. data/Manifest +0 -19
  46. data/howcast-0.7.3.gem +0 -0
  47. data/spec/string_matchers_helper.rb +0 -22
@@ -1,97 +1,38 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
1
  # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "howcast/version"
5
4
 
6
5
  Gem::Specification.new do |s|
7
- s.name = %q{howcast}
8
- s.version = "0.7.4"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Jingshen Jimmy Zhang", "Ian Smith-Heisters"]
12
- s.date = %q{2010-04-05}
6
+ s.name = "howcast"
7
+ s.version = Howcast::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Jingshen Jimmy Zhang", "Ian Smith-Heisters", "Juris Galang"]
10
+ s.email = ["support@howcast.com"]
11
+ s.homepage = "http://api.howcast.com/"
12
+ s.summary = %q{Ruby-bindings for the Howcast API}
13
13
  s.description = %q{ Howcast offers an Application Programming Interface (API) which allows
14
- developers to build applications that interface with Howcast. The Howcast
15
- API is RESTful (REpresentational State Transfer) and users of this API will
16
- be able: 1) Retreive detailed information about a single video, including
17
- metadata such as title, description, video views, rating etc; 2) Retrieve a
18
- list of videos restricted by a set of filters offered by Howcast and sorted
19
- using several metrics that you can specify (most recent, most views, etc);
20
- 3) Search for video; 4) And much more. Note: Before you can use our APIs,
21
- you must register an API key, that is submitted with each request.
22
- }
23
- s.email = %q{support@howcast.com}
24
- s.extra_rdoc_files = [
25
- "README.markdown"
26
- ]
27
- s.files = [
28
- "CHANGELOG",
29
- "License.txt",
30
- "Manifest",
31
- "README.markdown",
32
- "Rakefile",
33
- "VERSION",
34
- "howcast-0.7.3.gem",
35
- "howcast.gemspec",
36
- "lib/howcast.rb",
37
- "lib/howcast/client.rb",
38
- "lib/howcast/client/base.rb",
39
- "lib/howcast/client/category.rb",
40
- "lib/howcast/client/homepage.rb",
41
- "lib/howcast/client/marker.rb",
42
- "lib/howcast/client/playlist.rb",
43
- "lib/howcast/client/search.rb",
44
- "lib/howcast/client/user.rb",
45
- "lib/howcast/client/video.rb",
46
- "lib/howcast/errors.rb",
47
- "lib/howcast/logging.rb",
48
- "spec/howcast/client/base_spec.rb",
49
- "spec/howcast/client/category_spec.rb",
50
- "spec/howcast/client/homepage_spec.rb",
51
- "spec/howcast/client/playlist_spec.rb",
52
- "spec/howcast/client/search_spec.rb",
53
- "spec/howcast/client/user_spec.rb",
54
- "spec/howcast/client/video_spec.rb",
55
- "spec/output_capture_helper.rb",
56
- "spec/spec.opts",
57
- "spec/spec_helper.rb",
58
- "spec/string_matchers_helper.rb",
59
- "spec/xml_fixtures_helper.rb",
60
- "tasks/github.rake"
61
- ]
62
- s.homepage = %q{http://github.com/howcast/howcast-gem}
63
- s.rdoc_options = ["--charset=UTF-8"]
64
- s.require_paths = ["lib"]
65
- s.rubygems_version = %q{1.3.6}
66
- s.summary = %q{Howcast API Ruby Wrapper}
67
- s.test_files = [
68
- "spec/howcast/client/base_spec.rb",
69
- "spec/howcast/client/category_spec.rb",
70
- "spec/howcast/client/homepage_spec.rb",
71
- "spec/howcast/client/playlist_spec.rb",
72
- "spec/howcast/client/search_spec.rb",
73
- "spec/howcast/client/user_spec.rb",
74
- "spec/howcast/client/video_spec.rb",
75
- "spec/output_capture_helper.rb",
76
- "spec/spec_helper.rb",
77
- "spec/string_matchers_helper.rb",
78
- "spec/xml_fixtures_helper.rb"
79
- ]
14
+ developers to build applications that interface with Howcast. The Howcast
15
+ API is RESTful (REpresentational State Transfer) and users of this API will
16
+ be able: 1) Retreive detailed information about a single video, including
17
+ metadata such as title, description, video views, rating etc; 2) Retrieve a
18
+ list of videos restricted by a set of filters offered by Howcast and sorted
19
+ using several metrics that you can specify (most recent, most views, etc);
20
+ 3) Search for video; 4) And much more. Note: Before you can use our APIs,
21
+ you must register an API key, that is submitted with each request.
22
+ }
80
23
 
81
- if s.respond_to? :specification_version then
82
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
83
- s.specification_version = 3
24
+ s.rubyforge_project = "howcast"
84
25
 
85
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
86
- s.add_runtime_dependency(%q<hpricot>, [">= 0"])
87
- s.add_development_dependency(%q<rspec>, [">= 0"])
88
- else
89
- s.add_dependency(%q<hpricot>, [">= 0"])
90
- s.add_dependency(%q<rspec>, [">= 0"])
91
- end
92
- else
93
- s.add_dependency(%q<hpricot>, [">= 0"])
94
- s.add_dependency(%q<rspec>, [">= 0"])
95
- end
26
+ s.files = `git ls-files`.split("\n")
27
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
28
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
29
+ s.require_paths = ["lib"]
30
+
31
+ s.add_runtime_dependency(%q<hpricot>, [">= 0"])
32
+ s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
33
+
34
+ #s.add_development_dependency(%q<jeweler>, [">= 0"])
35
+ s.add_development_dependency(%q<rake>, [">= 0"])
36
+ s.add_development_dependency(%q<rspec>, [">= 0"])
37
+ s.add_development_dependency(%q<ruby-debug>, [">= 0"])
96
38
  end
97
-
@@ -1,6 +1,6 @@
1
1
  module Howcast; end
2
2
 
3
- %w(howcast/client howcast/errors howcast/logging).each do |dependency|
3
+ %w(howcast/version howcast/client howcast/errors howcast/logging).each do |dependency|
4
4
  require(File.expand_path(File.join(File.dirname(__FILE__), dependency)))
5
5
  end
6
6
 
@@ -2,6 +2,6 @@
2
2
  class Howcast::Client
3
3
  end
4
4
 
5
- %w(client/base client/video client/search client/category client/marker client/user client/homepage client/playlist).each do |dependency|
5
+ %w(client/utils client/base client/video client/search client/category client/marker client/user client/homepage client/playlist client/type).each do |dependency|
6
6
  require(File.expand_path(File.join(File.dirname(__FILE__), dependency)))
7
7
  end
@@ -22,10 +22,16 @@
22
22
  #++
23
23
 
24
24
  require 'rubygems'
25
+ require 'benchmark'
26
+ require 'timeout'
25
27
  require 'hpricot'
28
+ require 'nokogiri'
26
29
  require 'open-uri'
27
30
  require 'uri'
28
31
 
32
+ require File.expand_path(File.join(File.dirname(__FILE__), '../hpricot/elements'))
33
+ require File.expand_path(File.join(File.dirname(__FILE__), '../ext/string'))
34
+
29
35
  class Howcast::Client
30
36
  attr_accessor :key
31
37
 
@@ -56,210 +62,248 @@ class Howcast::Client
56
62
  end
57
63
 
58
64
  def base_uri
59
- @base_uri ||= URI.parse("http://www.howcast.com")
65
+ @base_uri ||= URI.parse("http://api.howcast.com")
60
66
  end
61
67
  end
68
+
62
69
  protected
63
- # Establishes a connection with the Howcast API. Will auto append the api key, that was used
64
- # to instantiate the Client object, to the URL
65
- #
66
- # === Inputs
67
- #
68
- # * <tt>relative_path</tt> -- should be the path after <tt>http://www.howcast.com/</tt>
69
- #
70
- # === Outputs
71
- #
72
- # Hpricot object encapsulating the xml returned from the Howcast API
73
- #
74
- # === Exceptions
75
- #
76
- # * <tt>Howcast::ApiNotFound</tt> -- raised if the requested +relative_path+ is malformed or not available (404)
77
- # * <tt>Howcast::ApiKeyNotFound</tt> -- raised if the +api_key+ is invalid
78
- #
79
- # === Examples
80
- #
81
- # Get the Hpricot data for most recent howcast studios videos
82
- # establish_connection("videos/most_recent/howcast_studios.xml")
83
- def establish_connection(relative_path_and_query)
84
- uri = self.class.base_uri.dup
85
- relative_path_and_query = '/' + relative_path_and_query unless relative_path_and_query[0] == '/'
86
- uri.path, uri.query = *relative_path_and_query.split('?')
87
- h = Hpricot.XML(open(url = attach_api_key(uri)))
88
- Howcast.log.info "Established connection with: '#{url}'"
89
- raise Howcast::ApiKeyNotFound if h.at(:error) && h.at(:error).inner_html.match(/Invalid API Key/)
90
- return h
70
+ # Establishes a connection with the Howcast API. Will auto append the api key, that was used
71
+ # to instantiate the Client object, to the URL
72
+ #
73
+ # === Inputs
74
+ #
75
+ # * <tt>relative_path</tt> -- should be the path after <tt>http://www.howcast.com/</tt>
76
+ #
77
+ # === Outputs
78
+ #
79
+ # Hpricot object encapsulating the xml returned from the Howcast API
80
+ #
81
+ # === Exceptions
82
+ #
83
+ # * <tt>Howcast::ApiNotFound</tt> -- raised if the requested +relative_path+ is malformed or not available (404)
84
+ # * <tt>Howcast::ApiKeyNotFound</tt> -- raised if the +api_key+ is invalid
85
+ #
86
+ # === Examples
87
+ #
88
+ # Get the Hpricot data for most recent howcast studios videos
89
+ # establish_connection("videos/most_recent/howcast_studios.xml")
90
+ def establish_connection(relative_path_and_query)
91
+ begin
92
+ uri = self.class.base_uri.dup
93
+ relative_path_and_query = '/' + relative_path_and_query unless relative_path_and_query[0] == '/'
94
+ uri.path, uri.query = *relative_path_and_query.split('?')
95
+
96
+ doc = nil
97
+ time = Benchmark.realtime do
98
+ doc = Hpricot.XML open(attach_api_key uri)
99
+ end
100
+ Howcast.log.info "Established connection with: '#{uri.to_s}' after #{time * 1000}ms"
101
+
102
+ raise Howcast::ApiKeyNotFound \
103
+ if doc.at(:err) && doc.at(:err)['msg'].match(/Invalid API Key/)
104
+
105
+ doc
106
+ rescue Timeout::Error
107
+ raise Howcast::ApiError, "Timed-out after #{time * 1000}ms while attempting to access the API at #{uri.to_s}"
91
108
  rescue URI::InvalidURIError
92
- raise Howcast::ApiNotFound.new("Invalid URL #{url.inspect} requested. Refer to the Howcast API for supported URL's")
93
- rescue OpenURI::HTTPError => boom
94
- raise Howcast::ApiError.new("HTTP error #{boom.message} accessing the API. Refer to the Howcast API for supported URL's")
109
+ raise Howcast::ApiNotFound, "Invalid URL #{uri.to_s} requested."
110
+ rescue OpenURI::HTTPError => e
111
+ raise Howcast::ApiError, "HTTP error #{e.message} accessing the API at #{uri.to_s}."
95
112
  end
96
-
97
- # Parses the xml for a single item from +xml+ and creates a new +klass+ object
98
- #
99
- # === Inputs
100
- #
101
- # * <tt>xml</tt> -- See below for a sample xml input
102
- # * <tt>klass</tt> -- Class to create - Video | Category supported
103
- #
104
- # Sample input xml
105
- # <video>
106
- # <id>1086</id>
107
- # <rating>96</rating>
108
- # <title>How To Choose a Paintbrush</title>
109
- # <category-id>19</category-id>
110
- # <description>
111
- # <![CDATA[Yes, you could just use your fingers, but selecting the best brushes for your painting might achieve a slightly more grown-up result.]]>
112
- # </description>
113
- # <views>362</views>
114
- # <permalink>http://www.howcast.com/guides/1086-How-To-Choose-a-Paintbrush</permalink>
115
- # <username>michaelrsanchez</username>
116
- # <created-at>Thu, 20 Dec 2007 14:14:58 -0800</created-at>
117
- # </video>
118
- #
119
- # === Outputs
120
- #
121
- # +klass+ object with initialized attributes
122
- def parse_single_xml(xml, klass)
123
- hash = {}
124
- klass.attr_accessors.each do |attribute|
125
- node_name = attribute.to_s.gsub("_", "-") # xml schema uses hyphens for spaces, but ruby uses underscores
126
- if node_name == "category-hierarchy"
127
- hash[attribute] = category_hierarchy_for(xml) unless xml.at(node_name).nil?
128
- elsif node_name == "ingredients"
129
- hash[attribute] = ingredients_for(xml) unless xml.at(node_name).nil?
130
- elsif node_name == "markers"
131
- hash[attribute] = markers_for(xml) unless xml.at(node_name).nil?
132
- elsif node_name == "related-videos"
133
- hash[attribute] = related_videos_for(xml) unless xml.at(node_name).nil?
134
- elsif node_name == "videos"
135
- hash[attribute] = videos_for(xml) unless xml.at(node_name).nil?
136
- elsif node_name == "playlist-thumbnail-url"
137
- # TO DO: Resolve this hack, xml attributes aren't named consistently
138
- hash[attribute] = !xml.at(node_name).nil? ? xml.at(node_name).inner_text.strip : xml.at("thumbnail-url").inner_text.strip
139
- else
140
- hash[attribute] = !xml.at(node_name).nil? ? xml.at(node_name).inner_text.strip : ""
141
- end
113
+ end
114
+
115
+ # Parses the xml for a single item from +xml+ and creates a new +klass+ object
116
+ #
117
+ # === Inputs
118
+ #
119
+ # * <tt>xml</tt> -- See below for a sample xml input
120
+ # * <tt>klass</tt> -- Class to create - Video | Category supported
121
+ #
122
+ # Sample input xml
123
+ # <video>
124
+ # <id>1086</id>
125
+ # <rating>96</rating>
126
+ # <title>How To Choose a Paintbrush</title>
127
+ # <category-id>19</category-id>
128
+ # <description>
129
+ # <![CDATA[Yes, you could just use your fingers, but selecting the best brushes for your painting might achieve a slightly more grown-up result.]]>
130
+ # </description>
131
+ # <views>362</views>
132
+ # <permalink>http://www.howcast.com/guides/1086-How-To-Choose-a-Paintbrush</permalink>
133
+ # <username>michaelrsanchez</username>
134
+ # <created-at>Thu, 20 Dec 2007 14:14:58 -0800</created-at>
135
+ # </video>
136
+ #
137
+ # === Outputs
138
+ #
139
+ # +klass+ object with initialized attributes
140
+ def parse_single_xml(xml, klass)
141
+ hash = { }
142
+ klass.attr_accessors.each do |attribute|
143
+ node_name = attribute.to_s.gsub("_", "-") # xml schema uses hyphens for spaces, but ruby uses underscores
144
+ if node_name == "category-hierarchy"
145
+ hash[attribute] = category_hierarchy_for(xml) unless xml.at(node_name).nil?
146
+ elsif node_name == "playlist-memberships"
147
+ hash[attribute] = playlist_memberships_for(xml) unless xml.at(node_name).nil?
148
+ elsif node_name == "ingredients"
149
+ hash[attribute] = ingredients_for(xml) unless xml.at(node_name).nil?
150
+ elsif node_name == "markers"
151
+ hash[attribute] = markers_for(xml) unless xml.at(node_name).nil?
152
+ elsif node_name == "related-videos"
153
+ hash[attribute] = related_videos_for(xml) unless xml.at(node_name).nil?
154
+ elsif node_name == "videos"
155
+ hash[attribute] = videos_for(xml) unless xml.at(node_name).nil?
156
+ elsif node_name == "playlist-thumbnail-url"
157
+ # TODO: Resolve this hack, xml attributes aren't named consistently
158
+ hash[attribute] = !xml.at(node_name).nil? ? xml.at(node_name).inner_text.strip : xml.at("thumbnail-url").inner_text.strip
159
+ elsif node_name == "type"
160
+ hash[attribute] = type_for(xml) unless xml.at(node_name).nil?
161
+ elsif %w{ ads-allowed mature-content }.include? node_name
162
+ hash[attribute] = !xml.at(node_name).nil? ? "true" : ""
163
+ else
164
+ hash[attribute] = !xml.at(node_name).nil? ? xml.at(node_name).inner_text.strip : ""
142
165
  end
143
- hash.values.all?{|v| v==""} ? nil : klass.new(hash)
144
- end
145
-
146
- # Creates parameters to append to a uri
147
- #
148
- # === Inputs
149
- #
150
- # Options are:
151
- # * <tt>:page</tt> -- the page number
152
- # * <tt>:use_ampersand</tt> -- boolean to return ampersand (defaults to false)
153
- #
154
- # === Outputs
155
- #
156
- # A valid suffix string to append to the end of a url
157
- #
158
- # === Examples
159
- #
160
- # "?page=2"
161
- # uri_suffix(:page => 2)
162
- # "&page=2"
163
- # uri_suffix(:page => 3, :use_ampersand => true)
164
- def uri_suffix(opts)
165
- opts && opts[:page] ? "#{opts[:use_ampersand] ? '&' : '?'}page=#{opts[:page]}" : ""
166
166
  end
167
+ hash.values.all?{ |v| (v == "") or (v.respond_to?(:empty?) and v.empty?) } ? nil : klass.new(hash)
168
+ end
167
169
 
168
- # Appends the api key to a uri and returns the appended uri
169
- #
170
- # === Inputs
171
- #
172
- # * <tt>uri</tt> -- the URI object to append the api_key to
173
- #
174
- # === Outputs
175
- #
176
- # The uri with the api_key appended to the query string
177
- #
178
- # === Examples
179
- #
180
- # attach_api_key(URI.parse("http://www.howcast.com")).to_s
181
- # => "http://www.howcast.com?api_key=APIKEYHERE"
182
- #
183
- # attach_api_key(URI.parse("http://www.howcast.com/videos/most_recent/all?page=2")).to_s
184
- # => "http://www.howcast.com/videos/most_recent/all?page=2&api_key=APIKEYHERE"
185
- def attach_api_key(uri)
186
- uri = uri.dup
187
- key = "api_key=#{self.key}"
188
- uri.query = uri.query.to_s.strip != "" ? uri.query+"&"+key : key
189
- uri
190
- end
170
+ # Creates parameters to append to a uri
171
+ #
172
+ # === Inputs
173
+ #
174
+ # Options are:
175
+ # * <tt>:page</tt> -- the page number
176
+ # * <tt>:use_ampersand</tt> -- boolean to return ampersand (defaults to false)
177
+ #
178
+ # === Outputs
179
+ #
180
+ # A valid suffix string to append to the end of a url
181
+ #
182
+ # === Examples
183
+ #
184
+ # "?page=2"
185
+ # uri_suffix(:page => 2)
186
+ # "&page=2"
187
+ # uri_suffix(:page => 3, :use_ampersand => true)
188
+ def uri_suffix(opts)
189
+ opts && opts[:page] ? "#{opts[:use_ampersand] ? '&' : '?'}page=#{opts[:page]}" : ""
190
+ end
191
+
192
+ # Appends the api key to a uri and returns the appended uri
193
+ #
194
+ # === Inputs
195
+ #
196
+ # * <tt>uri</tt> -- the URI object to append the api_key to
197
+ #
198
+ # === Outputs
199
+ #
200
+ # The uri with the api_key appended to the query string
201
+ #
202
+ # === Examples
203
+ #
204
+ # attach_api_key(URI.parse("http://www.howcast.com")).to_s
205
+ # => "http://www.howcast.com?api_key=APIKEYHERE"
206
+ #
207
+ # attach_api_key(URI.parse("http://www.howcast.com/videos/most_recent/all?page=2")).to_s
208
+ # => "http://www.howcast.com/videos/most_recent/all?page=2&api_key=APIKEYHERE"
209
+ def attach_api_key(uri)
210
+ uri = uri.dup
211
+ key = "api_key=#{self.key}"
212
+ uri.query = uri.query.to_s.strip != "" ? uri.query+"&"+key : key
213
+ uri
214
+ end
191
215
 
192
- # From merb/core_ext/hash.rb, line 87
193
- def hash_to_params hash
194
- params = ''
195
- stack = []
216
+ # From merb/core_ext/hash.rb, line 87
217
+ def hash_to_params hash
218
+ params = ''
219
+ stack = []
196
220
 
197
- hash.each do |k, v|
198
- if v.is_a?(Hash)
199
- stack << [k,v]
200
- else
201
- params << "#{k}=#{v}&"
202
- end
221
+ hash.keys.sort{ |a, b| a.to_s <=> b.to_s }.each do |k|
222
+ v = hash[k]
223
+ if v.is_a?(Hash)
224
+ stack << [k,v]
225
+ else
226
+ params << "#{k}=#{v}&"
203
227
  end
228
+ end
204
229
 
205
- stack.each do |parent, h|
206
- h.each do |k, v|
207
- if v.is_a?(Hash)
208
- stack << ["#{parent}[#{k}]", v]
209
- else
210
- params << "#{parent}[#{k}]=#{v}&"
211
- end
230
+ stack.each do |parent, h|
231
+ h.each do |k, v|
232
+ if v.is_a?(Hash)
233
+ stack << ["#{parent}[#{k}]", v]
234
+ else
235
+ params << "#{parent}[#{k}]=#{v}&"
212
236
  end
213
237
  end
214
-
215
- params.chop! # trailing &
216
- params
217
238
  end
239
+
240
+ params.chop! # trailing &
241
+ params
242
+ end
218
243
 
219
244
  private
220
- def category_hierarchy_for(xml)
221
- categories = []
222
- node = xml.at('category-hierarchy')
223
- node.children_of_type('category').each do |child|
224
- category = Category.new(:id => child['id'], :parent_id => child['parent_id'], :name => child.inner_text)
225
- categories << category
226
- end unless node.nil?
227
- categories
228
- end
229
-
230
- def ingredients_for(xml)
231
- ingredients = []
232
- node = xml.at('ingredients')
233
- node.children_of_type('ingredient').each do |child|
234
- ingredients << child.inner_text.strip
235
- end unless node.nil?
236
- ingredients
237
- end
238
-
239
- def markers_for(xml)
240
- markers = []
241
- node = xml.at('markers')
242
- node.children_of_type('marker').each do |child|
243
- markers << parse_single_xml(child, Marker)
244
- end unless node.nil?
245
- markers
246
- end
247
-
248
- def related_videos_for(xml)
249
- related = []
250
- node = xml.at('related-videos')
251
- node.children_of_type('video').each do |child|
252
- related << parse_single_xml(child, Video)
253
- end unless node.nil?
254
- related
255
- end
256
-
257
- def videos_for(xml)
258
- videos = []
259
- node = xml.at('videos')
260
- node.children_of_type('video').each do |child|
261
- videos << parse_single_xml(child, Video)
262
- end unless node.nil?
263
- videos
245
+ def category_hierarchy_for(xml)
246
+ categories = []
247
+ node = xml.at('category-hierarchy')
248
+ node.children_of_type('category').each do |child|
249
+ category = Category.new(:id => child['id'], :parent_id => child['parent_id'], :name => child.inner_text, :permalink => child['permalink'])
250
+ categories << category
251
+ end unless node.nil?
252
+ categories
253
+ end
254
+
255
+ def playlist_memberships_for(xml)
256
+ memberships = []
257
+ node = xml.at('playlist-memberships')
258
+ node.children_of_type('playlist').each do |child|
259
+ playlist = Playlist.new(:id => child['id'], :title => child.inner_text)
260
+ memberships << playlist
261
+ end unless node.nil?
262
+ memberships
263
+ end
264
+
265
+ def ingredients_for(xml)
266
+ ingredients = []
267
+ node = xml.at('ingredients')
268
+ node.children_of_type('ingredient').each do |child|
269
+ ingredients << child.inner_text.strip
270
+ end unless node.nil?
271
+ ingredients
272
+ end
273
+
274
+ def markers_for(xml)
275
+ markers = []
276
+ node = xml.at('markers')
277
+ node.children_of_type('marker').each do |child|
278
+ markers << parse_single_xml(child, Marker)
279
+ end unless node.nil?
280
+ markers
281
+ end
282
+
283
+ def type_for(xml)
284
+ node = xml.at('type')
285
+ if node['status'] && node['class']
286
+ Type.new(:name => node.inner_text.strip, :kind => node['class'], :status => node['status'])
287
+ else
288
+ node.inner_text.strip
264
289
  end
290
+ end
291
+
292
+ def related_videos_for(xml)
293
+ related = []
294
+ node = xml.at('related-videos')
295
+ node.children_of_type('video').each do |child|
296
+ related << parse_single_xml(child, Video)
297
+ end unless node.nil?
298
+ related
299
+ end
300
+
301
+ def videos_for(xml)
302
+ videos = []
303
+ node = xml.at('videos')
304
+ node.children_of_type('video').each do |child|
305
+ videos << parse_single_xml(child, Video)
306
+ end unless node.nil?
307
+ videos
308
+ end
265
309
  end