feedjira-podcast 0.9.8 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ sudo: false
1
2
  language: ruby
2
3
  rvm:
3
4
  - 2.2.1
5
+ - 2.2.3
6
+ cache: bundler
7
+ script: "bundle exec rake test"
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in feedjira-podcast.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -42,9 +42,9 @@ In cases where an element can repeat in a given context, the accessor will be pl
42
42
 
43
43
  #### Typecasting
44
44
 
45
- In nearly all cases the value types of podcast feed data is predictable. As such, the parser will cast values appropriately. In cases where a feed is malformed, the result of the type cast may be somewhat unpredictable, but no more so than the original data was. A bad value should never prevent parsing of the rest of the document, but it bust a specific value (e.g. a date that is human-readable but not parseable will end up as `nil` rather than fallback to an ambiguous `String` value).
45
+ In nearly all cases the value types of podcast feed data is predictable. As such, the parser will cast values appropriately. In cases where a feed is malformed, the result of the type cast may be somewhat unpredictable, but no more so than the original data was. A bad value should never prevent parsing of the rest of the document, but it could bust a specific value (e.g. a date that is human-readable but not parseable will end up as `nil` rather than fallback to an ambiguous `String` value).
46
46
 
47
- Date values are parsed as `Time` objects, number values become `Float` objects, hrefs, URIs and URLs are parsed using [Addressable](https://github.com/sporkmonger/addressable), and boolean values will return `true` or `false.`
47
+ Date values are parsed as `Time` objects, number values become `Float` objects, hrefs, URIs and URLs are parsed using [Addressable](https://github.com/sporkmonger/addressable), and boolean values will return `true` or `false`.
48
48
 
49
49
  In the case of the `<itunes:explicit>` tag, there are three mutually exclusive options, `"yes"`, `"clean"`, and any other value (representing `"no"`). This element gets expanded to two properties through parsing, `explicit?` and `clean?`, allowing both to be simple boolean values.
50
50
 
@@ -56,12 +56,16 @@ The parser will try its best to parse whatever data it has available, with a str
56
56
 
57
57
  Besides standard typecasting, the parser won't try to clean up any data. For example, many elements (such as `<description>`) explicitly allow only plaintext, but it is common for them to include markup in the wild. The parser will assume that markup to be plaintext, according to the spec. If you want to sanitize those parts of feeds, you should handle that on your end.
58
58
 
59
+ #### Parsing
60
+
61
+ During parsing, some aspects of the original feed will not be maintained in such a way that a functionally identical feed could be generated from the result. For example. `CDATA` declarations in XML elements are lost, as the parser converts them (correctly) to strings. If the resulting data is being used to generate feeds, wrapping values in CDATA would be the responsibility of the whatever is constructing the feed, based on the values of the strings. Similarly, Apple expects iTunes category tag values to include encoded ampersands, eg `TV &amp; Film`. Parsing the feed will decode those values (to `TV & Film`), so they would have to be re-encoded before being used somewhere that iTunes would be reading from.
62
+
59
63
  ### More Information
60
64
 
61
65
  For more detailed information about specific aspects of feeds, how they are spec'd, and how they are handled by the parser, see the [wiki](https://github.com/scour/feedjira-podcast/wiki).
62
66
 
63
67
  ## In Progress
64
68
 
65
- Coverage of RSS, iTunes, and the other common constituents of podcast feeds is very high, but there are some bits that need to be addressed. Several rarely-used RSS elements (`<cloud>`, `<rating>`, etc) are not supported. Due to how they can be nested `<itunes:category>` is also a work in progress. Currently only top-level categories are available. More esoteric elements, such a host-specific tags, or various parts of Dublin Core, are added based on their prevalence in real world feeds.
69
+ Coverage of RSS, iTunes, and the other common constituents of podcast feeds is very high, but there are some bits that need to be addressed. Several rarely-used RSS elements (`<rating>`, etc) are not supported. More esoteric elements, such a host-specific tags, or various parts of Dublin Core, are added based on their prevalence in real world feeds.
66
70
 
67
71
  Experimental feed elements may be added over time, but they should be used with caution until they reach a critical mass or become standardized.
data/Rakefile CHANGED
@@ -1,8 +1,12 @@
1
- require 'bundler/gem_tasks'
2
- require 'rake/testtask'
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
3
 
4
4
  Rake::TestTask.new(:test) do |t|
5
- t.pattern = 'test/**/test_*.rb'
5
+ t.pattern = "test/**/test_*.rb"
6
6
  end
7
7
 
8
- task default: :test
8
+ require "rubocop/rake_task"
9
+ RuboCop::RakeTask.new(:analyze)
10
+
11
+ task checks: [:test, :analyze]
12
+ task default: :checks
@@ -1,33 +1,34 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
2
+ lib = File.expand_path("../lib", __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'feedjira/podcast/version'
4
+ require "feedjira/podcast/version"
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = 'feedjira-podcast'
7
+ spec.name = "feedjira-podcast"
8
8
  spec.version = Feedjira::Podcast::VERSION
9
- spec.authors = ['Chris Kalafarski']
10
- spec.email = ['chris@farski.com']
9
+ spec.authors = ["Chris Kalafarski"]
10
+ spec.email = ["chris@farski.com"]
11
11
 
12
- spec.summary = %q{Podcast feed parsing with Feedjira}
13
- spec.description = %q{Podcast feed parsing with Feedjira}
14
- spec.homepage = 'https://github.com/scour/feedjira-podcast'
15
- spec.license = 'MIT'
12
+ spec.summary = "Podcast feed parsing with Feedjira"
13
+ spec.description = "Podcast feed parsing with Feedjira"
14
+ spec.homepage = "https://github.com/scour/feedjira-podcast"
15
+ spec.license = "MIT"
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = 'exe'
18
+ spec.bindir = "exe"
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
- spec.require_paths = ['lib']
20
+ spec.require_paths = ["lib"]
21
21
 
22
22
  if spec.respond_to?(:metadata)
23
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
23
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
24
24
  end
25
25
 
26
- spec.add_development_dependency 'bundler', '~> 1.8'
27
- spec.add_development_dependency 'rake', '~> 10.0'
28
- spec.add_development_dependency 'minitest', '~> 5.5'
29
- spec.add_development_dependency 'coveralls', '~> 0'
26
+ spec.add_development_dependency "bundler", "~> 1.8"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "minitest", "~> 5.5"
29
+ spec.add_development_dependency "coveralls", "~> 0"
30
+ spec.add_development_dependency "rubocop", "~> 0"
30
31
 
31
- spec.add_runtime_dependency 'feedjira', '~> 2.0'
32
- spec.add_runtime_dependency 'addressable', '~> 2.3'
32
+ spec.add_runtime_dependency "feedjira", "~> 2.0"
33
+ spec.add_runtime_dependency "addressable", "~> 2.3"
33
34
  end
@@ -9,7 +9,6 @@ module Feedjira
9
9
  include ::Feedjira::Podcast::Item::Apple
10
10
  include ::Feedjira::Podcast::Item::Dublin
11
11
  include ::Feedjira::Podcast::Item::Content
12
-
13
12
  end
14
13
  end
15
14
  end
@@ -1,39 +1,34 @@
1
- require 'feedjira'
2
- require 'addressable/uri'
3
-
4
- require 'feedjira/podcast/version'
5
-
6
- require 'feedjira/podcast/channel/image'
7
- require 'feedjira/podcast/channel/skip_hours'
8
- require 'feedjira/podcast/channel/skip_days'
9
- require 'feedjira/podcast/channel/text_input'
10
- require 'feedjira/podcast/channel/apple_owner'
11
- require 'feedjira/podcast/channel/apple_subcategory'
12
- require 'feedjira/podcast/channel/apple_category'
13
-
14
- require 'feedjira/podcast/channel/required'
15
- require 'feedjira/podcast/channel/optional'
16
- require 'feedjira/podcast/channel/atom'
17
- require 'feedjira/podcast/channel/feedburner'
18
- require 'feedjira/podcast/channel/apple'
19
- require 'feedjira/podcast/channel/syndication'
20
-
21
- require 'feedjira/podcast/item/guid'
22
- require 'feedjira/podcast/item/source'
23
-
24
- require 'feedjira/podcast/item/required'
25
- require 'feedjira/podcast/item/optional'
26
- require 'feedjira/podcast/item/apple'
27
- require 'feedjira/podcast/item/dublin'
28
- require 'feedjira/podcast/item/content'
29
-
30
- require 'feedjira/parser/podcast_item'
31
- require 'feedjira/parser/podcast'
32
-
33
- module Feedjira
34
- module Podcast
35
- # Your code goes here...
36
- end
37
- end
1
+ require "feedjira"
2
+ require "addressable/uri"
3
+
4
+ require "feedjira/podcast/version"
5
+
6
+ require "feedjira/podcast/channel/cloud"
7
+ require "feedjira/podcast/channel/image"
8
+ require "feedjira/podcast/channel/skip_hours"
9
+ require "feedjira/podcast/channel/skip_days"
10
+ require "feedjira/podcast/channel/text_input"
11
+ require "feedjira/podcast/channel/apple_owner"
12
+ require "feedjira/podcast/channel/apple_subcategory"
13
+ require "feedjira/podcast/channel/apple_category"
14
+
15
+ require "feedjira/podcast/channel/required"
16
+ require "feedjira/podcast/channel/optional"
17
+ require "feedjira/podcast/channel/atom"
18
+ require "feedjira/podcast/channel/feedburner"
19
+ require "feedjira/podcast/channel/apple"
20
+ require "feedjira/podcast/channel/syndication"
21
+
22
+ require "feedjira/podcast/item/guid"
23
+ require "feedjira/podcast/item/source"
24
+
25
+ require "feedjira/podcast/item/required"
26
+ require "feedjira/podcast/item/optional"
27
+ require "feedjira/podcast/item/apple"
28
+ require "feedjira/podcast/item/dublin"
29
+ require "feedjira/podcast/item/content"
30
+
31
+ require "feedjira/parser/podcast_item"
32
+ require "feedjira/parser/podcast"
38
33
 
39
34
  Feedjira::Feed.add_feed_class(Feedjira::Parser::Podcast)
@@ -2,35 +2,7 @@ module Feedjira
2
2
  module Podcast
3
3
  module Channel
4
4
  module Apple
5
- def self.included(base)
6
-
7
- base.element :"itunes:author", as: :itunes_author
8
-
9
- base.element :"itunes:block", as: :_itunes_block
10
-
11
- base.elements :"itunes:category", as: :itunes_categories, class: AppleCategory
12
-
13
- base.element :"itunes:image", as: :itunes_image_href, value: :href do |href|
14
- Addressable::URI.parse(href)
15
- end
16
-
17
- base.element :"itunes:explicit", as: :_itunes_explicit
18
- base.element :"itunes:complete", as: :_itunes_complete
19
-
20
- base.element :"itunes:new_feed_url", as: :itunes_new_feed_url do |url|
21
- Addressable::URI.parse(url)
22
- end
23
-
24
- base.element :"itunes:owner", as: :_itunes_owner, class: AppleOwner
25
- base.element :"itunes:subtitle", as: :itunes_subtitle
26
- base.element :"itunes:summary", as: :itunes_summary
27
-
28
- # Legacy support
29
-
30
- base.element :"itunes:keywords", as: :itunes_keywords do |keywords|
31
- keywords.split(',').map(&:strip).select { |k| !k.empty? }
32
- end
33
-
5
+ module InstanceMethods
34
6
  def itunes
35
7
  @itunes ||= Struct.new(
36
8
  :author,
@@ -68,19 +40,23 @@ module Feedjira
68
40
  end
69
41
 
70
42
  def itunes_block
71
- @itunes_block ||= (_itunes_block == 'yes')
43
+ @itunes_block ||= (_itunes_block == "yes")
44
+ end
45
+
46
+ def itunes_categories
47
+ @itunes_categories ||= _itunes_categories.select(&:valid?)
72
48
  end
73
49
 
74
50
  def itunes_complete
75
- @itunes_complete ||= (_itunes_complete == 'yes')
51
+ @itunes_complete ||= (_itunes_complete == "yes")
76
52
  end
77
53
 
78
54
  def itunes_explicit
79
- @itunes_explicit ||= (_itunes_explicit == 'yes')
55
+ @itunes_explicit ||= (_itunes_explicit == "yes")
80
56
  end
81
57
 
82
58
  def itunes_clean
83
- @itunes_clean ||= (_itunes_explicit == 'clean')
59
+ @itunes_clean ||= (_itunes_explicit == "clean")
84
60
  end
85
61
 
86
62
  def itunes_owner
@@ -89,7 +65,37 @@ module Feedjira
89
65
  _itunes_owner && _itunes_owner.name,
90
66
  )
91
67
  end
68
+ end
69
+
70
+ def self.included(base)
71
+ base.include(InstanceMethods)
72
+
73
+ base.element :"itunes:author", as: :itunes_author
74
+
75
+ base.element :"itunes:block", as: :_itunes_block
76
+
77
+ base.elements :"itunes:category", as: :_itunes_categories, class: AppleCategory
78
+
79
+ base.element :"itunes:image", as: :itunes_image_href, value: :href do |href|
80
+ Addressable::URI.parse(href)
81
+ end
82
+
83
+ base.element :"itunes:explicit", as: :_itunes_explicit
84
+ base.element :"itunes:complete", as: :_itunes_complete
85
+
86
+ base.element :"itunes:new_feed_url", as: :itunes_new_feed_url do |url|
87
+ Addressable::URI.parse(url)
88
+ end
89
+
90
+ base.element :"itunes:owner", as: :_itunes_owner, class: AppleOwner
91
+ base.element :"itunes:subtitle", as: :itunes_subtitle
92
+ base.element :"itunes:summary", as: :itunes_summary
92
93
 
94
+ # Legacy support
95
+
96
+ base.element :"itunes:keywords", as: :itunes_keywords do |keywords|
97
+ keywords.split(",").map(&:strip).select { |k| !k.empty? }
98
+ end
93
99
  end
94
100
  end
95
101
  end
@@ -5,10 +5,69 @@ module Feedjira
5
5
  include SAXMachine
6
6
  include FeedUtilities
7
7
 
8
+ CATEGORIES = {
9
+ "Arts" => [
10
+ "Design",
11
+ "Fashion & Beauty",
12
+ "Food",
13
+ "Literature",
14
+ "Performing Arts",
15
+ "Visual Arts"
16
+ ],
17
+ "Business" => [
18
+ "Business News",
19
+ "Careers",
20
+ "Investing",
21
+ "Management & Marketing",
22
+ "Shopping"
23
+ ],
24
+ "Comedy" => [],
25
+ "Education" => [
26
+
27
+ ],
28
+ "Games & Hobbies" => [
29
+
30
+ ],
31
+ "Government & Organizations" => [
32
+
33
+ ],
34
+ "Health" => [
35
+
36
+ ],
37
+ "Kids & Family" => [],
38
+ "Music" => [],
39
+ "News & Politics" => [],
40
+ "Religion & Spirituality" => [
41
+
42
+ ],
43
+ "Science & Medicine" => [
44
+
45
+ ],
46
+ "Society & Culture" => [
47
+
48
+ ],
49
+ "Sports & Recreation" => [
50
+
51
+ ],
52
+ "Technology" => [
53
+
54
+ ],
55
+ "TV & Film" => []
56
+ }
57
+
8
58
  attribute :text
9
59
 
10
- element :"itunes:category", as: :subcategory, class: AppleSubcategory
60
+ elements :"itunes:category", as: :subcategories, class: AppleSubcategory
61
+
62
+ def subcategory
63
+ if subcategories.first && subcategories.first.valid?(self)
64
+ subcategories.first
65
+ end
66
+ end
11
67
 
68
+ def valid?
69
+ CATEGORIES.include?(text)
70
+ end
12
71
  end
13
72
  end
14
73
  end
@@ -7,6 +7,9 @@ module Feedjira
7
7
 
8
8
  attribute :text
9
9
 
10
+ def valid?(category)
11
+ AppleCategory::CATEGORIES[category.text].include?(text)
12
+ end
10
13
  end
11
14
  end
12
15
  end
@@ -2,18 +2,7 @@ module Feedjira
2
2
  module Podcast
3
3
  module Channel
4
4
  module Atom
5
- def self.included(base)
6
-
7
- ['self', 'hub'].each do |rel|
8
- [:"atom:link", :"atom10:link"].each do |ns|
9
- base.element ns, with: { rel: rel }, as: "atom_#{rel}_link_href".to_sym, value: :href do |href|
10
- Addressable::URI.parse(href)
11
- end
12
- base.element ns, with: { rel: rel }, as: "atom_#{rel}_link_rel".to_sym, value: :rel
13
- base.element ns, with: { rel: rel }, as: "atom_#{rel}_link_type".to_sym, value: :type
14
- end
15
- end
16
-
5
+ module InstanceMethods
17
6
  def atom
18
7
  @atom ||= Struct.new(:link).new(atom_link)
19
8
  end
@@ -42,7 +31,20 @@ module Feedjira
42
31
  atom_hub_link_type,
43
32
  )
44
33
  end
34
+ end
35
+
36
+ def self.included(base)
37
+ base.include(InstanceMethods)
45
38
 
39
+ ["self", "hub"].each do |rel|
40
+ [:"atom:link", :"atom10:link"].each do |ns|
41
+ base.element ns, with: { rel: rel }, as: "atom_#{rel}_link_href".to_sym, value: :href do |href|
42
+ Addressable::URI.parse(href)
43
+ end
44
+ base.element ns, with: { rel: rel }, as: "atom_#{rel}_link_rel".to_sym, value: :rel
45
+ base.element ns, with: { rel: rel }, as: "atom_#{rel}_link_type".to_sym, value: :type
46
+ end
47
+ end
46
48
  end
47
49
  end
48
50
  end
@@ -0,0 +1,16 @@
1
+ module Feedjira
2
+ module Podcast
3
+ module Channel
4
+ class Cloud
5
+ include SAXMachine
6
+ include FeedUtilities
7
+
8
+ attribute :domain
9
+ attribute :port
10
+ attribute :path
11
+ attribute :registerProcedure
12
+ attribute :protocol
13
+ end
14
+ end
15
+ end
16
+ end
@@ -2,17 +2,19 @@ module Feedjira
2
2
  module Podcast
3
3
  module Channel
4
4
  module Feedburner
5
- def self.included(base)
6
-
7
- base.element :"feedburner:info", as: :feedburner_info_uri, value: :uri do |uri|
8
- Addressable::URI.parse(uri)
9
- end
10
-
5
+ module InstanceMethods
11
6
  def feedburner
12
7
  info = Struct.new(:uri).new(feedburner_info_uri)
13
8
  @feedburner ||= Struct.new(:info).new(info)
14
9
  end
10
+ end
11
+
12
+ def self.included(base)
13
+ base.include(InstanceMethods)
15
14
 
15
+ base.element :"feedburner:info", as: :feedburner_info_uri, value: :uri do |uri|
16
+ Addressable::URI.parse(uri)
17
+ end
16
18
  end
17
19
  end
18
20
  end