feedparser 1.1.1 → 1.2.0

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/Manifest.txt +36 -7
  3. data/README.md +145 -10
  4. data/Rakefile +1 -1
  5. data/lib/feedparser.rb +3 -0
  6. data/lib/feedparser/author.rb +39 -0
  7. data/lib/feedparser/builder/atom.rb +64 -17
  8. data/lib/feedparser/builder/json.rb +46 -10
  9. data/lib/feedparser/builder/rss.rb +94 -15
  10. data/lib/feedparser/feed.rb +30 -6
  11. data/lib/feedparser/generator.rb +34 -0
  12. data/lib/feedparser/item.rb +31 -6
  13. data/lib/feedparser/tag.rb +23 -0
  14. data/lib/feedparser/version.rb +2 -2
  15. data/test/feeds/books/nostarch.rss +125 -0
  16. data/test/feeds/books/oreilly.feedburner.atom +387 -0
  17. data/test/feeds/books/pragprog.rss +148 -0
  18. data/test/feeds/byparker.json +4 -4
  19. data/test/feeds/daringfireball.atom +8 -1
  20. data/test/feeds/daringfireball.json +28 -0
  21. data/test/feeds/googlegroups.atom +2 -2
  22. data/test/feeds/googlegroups2.atom +1 -1
  23. data/test/feeds/headius.atom +6 -5
  24. data/test/feeds/inessential.json +13 -4
  25. data/test/feeds/jsonfeed.json +8 -3
  26. data/test/feeds/{lambdatheultimate.rss2 → lambdatheultimate.rss} +0 -0
  27. data/test/feeds/{learnenough.atom → learnenough.feedburner.atom} +12 -9
  28. data/test/feeds/news/nytimes-blogs-bits.rss +333 -0
  29. data/test/feeds/news/nytimes-paul-krugman.rss +60 -0
  30. data/test/feeds/news/nytimes-tech.rss +653 -0
  31. data/test/feeds/news/nytimes-thomas-l-friedman.rss +80 -0
  32. data/test/feeds/news/nytimes.rss +607 -0
  33. data/test/feeds/news/washingtonpost-blogs-innovations.rss +183 -0
  34. data/test/feeds/news/washingtonpost-politics.rss +35 -0
  35. data/test/feeds/news/washingtonpost-world.rss +29 -0
  36. data/test/feeds/ongoing.atom +1619 -0
  37. data/test/feeds/osm/blog.openstreetmap.rss +252 -0
  38. data/test/feeds/osm/blogs.openstreetmap.rss +585 -0
  39. data/test/feeds/osm/mapbox.rss +1883 -0
  40. data/test/feeds/{railstutorial.atom → railstutorial.feedburner.atom} +28 -27
  41. data/test/feeds/{rubyflow.rss2 → rubyflow.feedburner.rss} +7 -3
  42. data/test/feeds/{rubymine.rss2 → rubymine.feedburner.rss} +5 -6
  43. data/test/feeds/scripting.rss +881 -0
  44. data/test/feeds/{sitepoint.rss2 → sitepoint.rss} +8 -9
  45. data/test/feeds/spec/atom/author.atom +48 -0
  46. data/test/feeds/spec/atom/authors.atom +70 -0
  47. data/test/feeds/spec/atom/categories.atom +66 -0
  48. data/test/feeds/spec/json/microblog.json +11 -2
  49. data/test/feeds/spec/json/tags.json +33 -0
  50. data/test/feeds/spec/rss/author.rss +41 -0
  51. data/test/feeds/spec/rss/categories.rss +64 -0
  52. data/test/feeds/spec/rss/creator.rss +38 -0
  53. data/test/feeds/{xkcd.rss2 → xkcd.rss} +0 -0
  54. data/test/helper.rb +21 -16
  55. data/test/test_atom.rb +1 -1
  56. data/test/test_atom_live.rb +1 -1
  57. data/test/test_authors.rb +26 -0
  58. data/test/test_books.rb +25 -0
  59. data/test/test_feeds.rb +4 -2
  60. data/test/test_rss.rb +4 -4
  61. data/test/test_tags.rb +25 -0
  62. metadata +39 -10
@@ -32,34 +32,50 @@ class JsonFeedBuilder
32
32
  feed.summary = h['description']
33
33
 
34
34
 
35
+ if h['author']
36
+ feed.authors << build_author( h['author'] )
37
+ end
38
+
35
39
 
36
- items = []
37
40
  h['items'].each do |hash_item|
38
- items << build_feed_item( hash_item )
41
+ feed.items << build_item( hash_item )
39
42
  end
40
- feed.items = items
41
43
 
42
44
  feed # return new feed
43
45
  end # method build_feed_from_json
44
46
 
45
47
 
48
+ def build_author( h )
49
+ author = Author.new
50
+
51
+ author.name = h['name']
52
+ author.url = h['url']
53
+ author.avatar = h['avatar']
54
+
55
+ author
56
+ end
57
+
46
58
 
47
- def build_feed_item( h )
59
+
60
+ def build_item( h )
48
61
  item = Item.new # Item.new
49
62
 
50
- item.guid = h['id']
51
- item.title = h['title']
52
- item.url = h['url']
63
+ item.guid = h['id']
64
+ item.title = h['title']
65
+ item.url = h['url']
66
+ item.external_url = h['external_url']
53
67
 
54
68
  ## convert date if present (from string to date type)
55
69
  date_published_str = h['date_published']
56
70
  if date_published_str
57
- item.published = DateTime.iso8601( date_published_str )
71
+ item.published_local = DateTime.iso8601( date_published_str )
72
+ item.published = item.published_local.utc
58
73
  end
59
74
 
60
75
  date_modified_str = h['date_modified']
61
76
  if date_modified_str
62
- item.updated = DateTime.iso8601( date_modified_str )
77
+ item.updated_local = DateTime.iso8601( date_modified_str )
78
+ item.updated = item.updated_local.utc
63
79
  end
64
80
 
65
81
 
@@ -67,8 +83,28 @@ class JsonFeedBuilder
67
83
  item.content_text = h['content_text']
68
84
  item.summary = h['summary']
69
85
 
86
+ if h['author']
87
+ item.authors << build_author( h['author'] )
88
+ end
89
+
90
+ if h['tags']
91
+ h['tags'].each do |json_tag|
92
+ item.tags << build_tag( json_tag )
93
+ end
94
+ end
95
+
70
96
  item
71
- end # method build_feed_item
97
+ end # method build_item
98
+
99
+
100
+ def build_tag( json_tag )
101
+ ## pp rss_cat
102
+ tag = Tag.new
103
+
104
+ tag.name = json_tag
105
+
106
+ tag
107
+ end # build_tag
72
108
 
73
109
 
74
110
  end # JsonFeedBuilder
@@ -35,25 +35,85 @@ class RssFeedBuilder
35
35
  feed.summary = handle_content( rss_feed.channel.description, 'feed.description => summary' ) # required
36
36
  feed.url = rss_feed.channel.link # required
37
37
 
38
- feed.updated = handle_date( rss_feed.channel.lastBuildDate, 'feed.lastBuildDate => updated' ) # optional
39
- feed.published = handle_date( rss_feed.channel.pubDate, 'feed.pubDate => published' ) # optional
38
+ feed.updated_local = handle_date( rss_feed.channel.lastBuildDate, 'feed.lastBuildDate => updated' ) # optional
39
+ feed.updated = feed.updated_local.utc if feed.updated_local
40
40
 
41
-
42
- feed.generator = rss_feed.channel.generator # optional
41
+ feed.published_local = handle_date( rss_feed.channel.pubDate, 'feed.pubDate => published' ) # optional
42
+ feed.published = feed.published_local.utc if feed.published_local
43
43
 
44
44
  logger.debug " rss | feed.generator >#{rss_feed.channel.generator}< : #{rss_feed.channel.generator.class.name}"
45
45
 
46
+ feed.generator.text = rss_feed.channel.generator # optional
47
+ feed.generator.name = feed.generator.text ## note: for now set also name/title to "unparsed" (content) line (may change in the future!!!)
48
+
49
+
50
+
51
+ ## check for managingEditor and/or webMaster
52
+
53
+ if rss_feed.channel.managingEditor
54
+ author = Author.new
55
+ author.text = rss_feed.channel.managingEditor.strip
56
+ author.name = author.text ## note: for now use "unparsed" (content) line also for name
57
+ feed.authors << author
58
+ end
59
+
60
+ if rss_feed.channel.webMaster
61
+ author = Author.new
62
+ author.text = rss_feed.channel.webMaster.strip
63
+ author.name = author.text ## note: for now use "unparsed" (content) line also for name
64
+ feed.authors << author
65
+ end
66
+
67
+
68
+ ## check for dublin core (dc) metadata
69
+
70
+ if rss_feed.channel.dc_creator
71
+ ## note: dc_creator wipes out authors if set with rss tag(s)
72
+ authors = []
73
+ authors << build_author_from_dublic_core_creator( rss_feed.channel.dc_creator )
74
+ feed.authors = authors
75
+ end
76
+
77
+
78
+ ### check for categories (tags)
79
+
80
+ rss_feed.channel.categories.each do |rss_cat|
81
+ feed.tags << build_tag( rss_cat )
82
+ end
83
+
46
84
 
47
- items = []
48
85
  rss_feed.items.each do |rss_item|
49
- items << build_feed_item( rss_item )
86
+ feed.items << build_item( rss_item )
50
87
  end
51
- feed.items = items
52
88
 
53
89
  feed # return new feed
54
90
  end
55
91
 
56
- def build_feed_item( rss_item )
92
+
93
+ def build_author_from_dublic_core_creator( dc_creator )
94
+ author = Author.new
95
+ author.text = dc_creator.strip
96
+ author.name = author.text # note: for now use "unparsed" creator line also for name (may change in the future!!!)
97
+ author
98
+ end
99
+
100
+
101
+
102
+ def build_tag( rss_cat )
103
+ ## pp rss_cat
104
+ tag = Tag.new
105
+
106
+ ## note: always strip leading n trailing spaces
107
+ ## and add if preset (not blank/empty e.g. not nil or "")
108
+ tag.name = rss_cat.content.strip if rss_cat.content
109
+ tag.domain = rss_cat.domain.strip if rss_cat.domain
110
+
111
+ tag
112
+ end # build_tag
113
+
114
+
115
+
116
+ def build_item( rss_item )
57
117
 
58
118
  item = Item.new
59
119
 
@@ -79,7 +139,8 @@ class RssFeedBuilder
79
139
  logger.debug " rss | item.content_encoded[0..40] >#{rss_item.content_encoded ? rss_item.content_encoded[0..40] : ''}< : #{rss_item.content_encoded.class.name}"
80
140
 
81
141
 
82
- item.updated = handle_date( rss_item.pubDate, 'item.pubDate => updated' )
142
+ item.updated_local = handle_date( rss_item.pubDate, 'item.pubDate => updated' )
143
+ item.updated = item.updated_local.utc if item.updated_local
83
144
 
84
145
 
85
146
  ## fix/todo: check if rss_item.guid present? !!!!
@@ -94,13 +155,31 @@ class RssFeedBuilder
94
155
  logger.warn " rss | item.guid.content missing !!!! - using link for guid"
95
156
  end
96
157
 
97
- ### todo: add support or authors (incl. dc:creator)
98
- ## <dc:creator>Dhaivat Pandya</dc:creator>
99
158
 
100
- # todo: categories
101
- # <category><![CDATA[Gems]]></category>
102
- # <category><![CDATA[Ruby]]></category>
103
- # <category><![CDATA[Ruby on Rails]]></category>
159
+ if rss_item.author
160
+ author = Author.new
161
+ author.text = rss_item.author.strip
162
+ author.name = author.text ## note: for now use "unparsed" (content) line also for name
163
+ item.authors << author
164
+ end
165
+
166
+
167
+ ## check for dublin core (dc) metadata
168
+
169
+ if rss_item.dc_creator
170
+ ## note: dc_creator wipes out authors if set with rss tag(s)
171
+ authors = []
172
+ authors << build_author_from_dublic_core_creator( rss_item.dc_creator )
173
+ item.authors = authors
174
+ end
175
+
176
+
177
+ ### check for categories (tags)
178
+
179
+ rss_item.categories.each do |rss_cat|
180
+ item.tags << build_tag( rss_cat )
181
+ end
182
+
104
183
 
105
184
  item
106
185
  end # method build_feed_item_from_rss
@@ -4,14 +4,27 @@ module FeedParser
4
4
 
5
5
  class Feed
6
6
 
7
- attr_accessor :format # e.g. atom|rss 2.0|etc.
7
+ attr_accessor :format # e.g. atom|rss 2.0|json etc.
8
8
  attr_accessor :title
9
9
  attr_accessor :url ## todo - add alias site_url/home_page_url/page_url - why? why not??
10
- attr_accessor :feed_url ## todo - add alias self_url - why? why not??
10
+ attr_accessor :feed_url
11
11
 
12
12
 
13
13
  attr_accessor :items
14
14
 
15
+ attr_accessor :authors
16
+ def authors?() @authors && @authors.size > 0; end
17
+ ## note: author? is an alias for authors?
18
+ alias :author? :authors?
19
+
20
+ ## add author shortcut e.g. equals authors[0] - for now only read only
21
+ ## fix: also add author= why? why not???
22
+ def author() @authors[0]; end
23
+
24
+
25
+ attr_accessor :tags
26
+ def tags?() @tags && @tags.size > 0; end
27
+
15
28
 
16
29
  def summary?() @summary.nil? == false; end
17
30
  attr_accessor :summary # e.g. description (rss)|subtitle (atom)
@@ -26,24 +39,35 @@ class Feed
26
39
  ## for now alias summary to subtitle
27
40
  alias :subtitle :summary
28
41
  alias :subtitle= :summary=
42
+ alias :subtitle? :summary?
29
43
 
30
44
 
31
45
 
32
46
  def updated?() @updated.nil? == false; end
33
- attr_accessor :updated # e.g. lastBuildDate (rss)|updated (atom)
47
+ attr_accessor :updated # e.g. lastBuildDate (rss)|updated (atom) -- always (converted) to utc
48
+ attr_accessor :updated_local # "unparsed" local datetime as in feed (NOT converted to utc)
34
49
 
35
50
  def published?() @published.nil? == false; end
36
- attr_accessor :published # e.g. pubDate (rss)\n/a (atom) -- note: published is basically an alias for created
51
+ attr_accessor :published # e.g. pubDate (rss)\n/a (atom) -- note: published is basically an alias for created
52
+ attr_accessor :published_local # "unparsed" local datetime as in feed (NOT converted to utc)
37
53
 
38
54
 
39
55
  attr_accessor :generator
40
- attr_accessor :generator_version # e.g. @version (atom)
41
- attr_accessor :generator_uri # e.g. @uri (atom) - use alias url/link ???
42
56
 
43
57
 
44
58
  ## fix:
45
59
  # add pretty printer/inspect (exclude object)
46
60
 
61
+
62
+ def initialize
63
+ ## note: make items, authors, tags empty arrays on startup (e.g. not nil)
64
+ @items = []
65
+ @authors = []
66
+ @tags = []
67
+
68
+ @generator = Generator.new
69
+ end
70
+
47
71
  end # class Feed
48
72
 
49
73
  end # module FeedParser
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ module FeedParser
4
+
5
+ class Generator
6
+
7
+ attr_accessor :name
8
+ ## note: title is an alias for name
9
+ alias :title :name
10
+ alias :title= :name=
11
+
12
+ attr_accessor :version
13
+
14
+ attr_accessor :url
15
+ ## note: uri is an alias for url
16
+ alias :uri :url ## add atom alias for uri - why? why not?
17
+ alias :uri= :url=
18
+
19
+
20
+ attr_accessor :text # note: holds "unparsed" text (content) line form rss:generator
21
+
22
+ def to_s
23
+ ## note: to_s - allows to use just generator in templates
24
+ ## will by default return name if present or as fallback "unparsed" text line
25
+ if @name ## not blank
26
+ @name
27
+ else
28
+ @text
29
+ end
30
+ end
31
+
32
+ end # class Generator
33
+
34
+ end # module FeedParser
@@ -6,6 +6,11 @@ class Item
6
6
 
7
7
  attr_accessor :title
8
8
  attr_accessor :url
9
+ attr_accessor :external_url
10
+
11
+ # note: related_url is an alias for external_url
12
+ alias :related_url :external_url ## link rel=related used in atom
13
+ alias :related_url= :external_url=
9
14
 
10
15
 
11
16
  ## note: only content/content_html should use html;
@@ -33,20 +38,40 @@ class Item
33
38
 
34
39
  def updated?() @updated.nil? == false; end
35
40
  attr_accessor :updated # pubDate (RSS)|updated (Atom)
41
+ attr_accessor :updated_local # "unparsed" local datetime as in feed (NOT converted to utc)
36
42
 
37
43
  def published?() @published.nil? == false; end
38
44
  attr_accessor :published # note: published is basically an alias for created
45
+ attr_accessor :published_local # "unparsed" local datetime as in feed (NOT converted to utc)
46
+
47
+
48
+
49
+ attr_accessor :id
50
+
51
+ ## note: guid is an alias for id
52
+ alias :guid :id
53
+ alias :guid= :id=
39
54
 
55
+ attr_accessor :authors
56
+ ## add author shortcut e.g. equals authors[0] - for now only read only
57
+ ## fix: also add author= why? why not???
58
+ def authors?() @authors && @authors.size > 0; end
59
+ ## note: author? is an alias for authors?
60
+ alias :author? :authors?
40
61
 
41
- attr_accessor :guid # todo: rename to id (use alias) ??
62
+ ## add author shortcut e.g. equals authors[0] - for now only read only
63
+ ## fix: also add author= why? why not???
64
+ def author() @authors[0]; end
42
65
 
43
- ## note: guid is an alias for id; todo/fix: reverse - make guid an alias of id
44
- alias :id :guid
45
- alias :id= :guid=
46
66
 
67
+ attr_accessor :tags
68
+ def tags?() @tags && @tags.size > 0; end
47
69
 
48
- ## todo: add author/authors
49
- ## todo: add category/categories
70
+ def initialize
71
+ ## note: make authors, tags empty arrays on startup (e.g. not nil)
72
+ @authors = []
73
+ @tags = []
74
+ end
50
75
 
51
76
  end # class Item
52
77
 
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ module FeedParser
4
+
5
+ class Tag
6
+
7
+ attr_accessor :name
8
+ ## note: title n term are aliases for name
9
+ alias :title :name
10
+ alias :title= :name=
11
+
12
+ alias :term :name
13
+ alias :term= :name=
14
+
15
+
16
+ attr_accessor :scheme ## use scheme_url -why? why not? is it always a url/uri??
17
+ ## note: domain (rss) is an alias for scheme (atom)
18
+ alias :domain :scheme
19
+ alias :domain= :scheme=
20
+
21
+ end # class Tag
22
+
23
+ end # module FeedParser
@@ -3,8 +3,8 @@
3
3
  module FeedParser
4
4
 
5
5
  MAJOR = 1
6
- MINOR = 1
7
- PATCH = 1
6
+ MINOR = 2
7
+ PATCH = 0
8
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
9
9
 
10
10
  def self.version
@@ -0,0 +1,125 @@
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <rss version="2.0" xml:base="https://www.nostarch.com/frontpage_comingsoon"
3
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
4
+ xmlns:atom="http://www.w3.org/2005/Atom">
5
+ <channel>
6
+ <title>No Starch Press - Coming Soon</title>
7
+ <link>https://www.nostarch.com/frontpage_comingsoon</link>
8
+ <description>Keep an eye on what&#039;s coming out down the line.</description>
9
+ <language>en</language>
10
+ <atom:link href="https://www.nostarch.com/feeds/comingsoon.xml?startat=tcpip" rel="self" type="application/rss+xml" />
11
+ <item>
12
+ <title>PoC||GTFO</title>
13
+ <link>https://www.nostarch.com/gtfo</link>
14
+ <description>&lt;div class=&quot;field field-name-field-image-cache field-type-image field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;a href=&quot;/gtfo&quot;&gt;&lt;img class=&quot;img-responsive&quot; src=&quot;https://www.nostarch.com/sites/default/files/styles/uc_product/public/GTFO_cover.png?itok=DUUsVJxz&quot; width=&quot;168&quot; height=&quot;225&quot; alt=&quot;PoC||GTFO&quot; title=&quot;PoC||GTFO&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.nostarch.com/gtfo&quot;&gt;PoC||GTFO&lt;/a&gt; is a compilation of the wildly popular hacker zine of the same name. Contributions range from humorous poems to deeply technical essays.&lt;/p&gt;
15
+ &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
16
+ <pubDate>Tue, 11 Apr 2017 23:50:54 +0000</pubDate>
17
+ <dc:creator>Caitlin Griffin</dc:creator>
18
+ <guid isPermaLink="false">423 at https://www.nostarch.com</guid>
19
+ </item>
20
+ <item>
21
+ <title>Coding iPhone Apps for Kids</title>
22
+ <link>https://www.nostarch.com/iphoneappsforkids</link>
23
+ <description>&lt;div class=&quot;field field-name-field-image-cache field-type-image field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;a href=&quot;/iphoneappsforkids&quot;&gt;&lt;img class=&quot;img-responsive&quot; src=&quot;https://www.nostarch.com/sites/default/files/styles/uc_product/public/CodingiPhoneAppsforKids_cover.png?itok=zfRGgmaL&quot; width=&quot;170&quot; height=&quot;225&quot; alt=&quot;Coding iPhone Apps for Kids&quot; title=&quot;Coding iPhone Apps for Kids&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;&lt;a href=&quot;/iphoneappsforkids&quot;&gt;Coding iPhone Apps for Kids&lt;/a&gt; teaches you how to program the iOS apps and games you’ve always wanted to make! &lt;i&gt;PDF Available Now!&lt;/i&gt;&lt;/p&gt;
24
+ &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
25
+ <pubDate>Fri, 11 Mar 2016 19:07:21 +0000</pubDate>
26
+ <dc:creator>Caitlin Griffin</dc:creator>
27
+ <guid isPermaLink="false">371 at https://www.nostarch.com</guid>
28
+ </item>
29
+ <item>
30
+ <title>Gray Hat C#</title>
31
+ <link>https://www.nostarch.com/grayhatcsharp</link>
32
+ <description>&lt;div class=&quot;field field-name-field-image-cache field-type-image field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;a href=&quot;/grayhatcsharp&quot;&gt;&lt;img class=&quot;img-responsive&quot; src=&quot;https://www.nostarch.com/sites/default/files/styles/uc_product/public/GrayHatC_cover.png?itok=oFuFmPd9&quot; width=&quot;170&quot; height=&quot;225&quot; alt=&quot;Gray Hat C#&quot; title=&quot;Gray Hat C#&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.nostarch.com/GrayHatCSharp&quot;&gt;Gray Hat C#&lt;/a&gt; shows you how to use C#&#039;s powerful set of core libraries to create and automate security tools. &lt;i&gt;PDF Available Now!&lt;/i&gt;&lt;/p&gt;
33
+ &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
34
+ <pubDate>Wed, 27 Jul 2016 18:35:33 +0000</pubDate>
35
+ <dc:creator>Caitlin Griffin</dc:creator>
36
+ <guid isPermaLink="false">389 at https://www.nostarch.com</guid>
37
+ </item>
38
+ <item>
39
+ <title>The Arduino Inventor&#039;s Guide</title>
40
+ <link>https://www.nostarch.com/sparkfunarduino</link>
41
+ <description>&lt;div class=&quot;field field-name-field-image-cache field-type-image field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;a href=&quot;/sparkfunarduino&quot;&gt;&lt;img class=&quot;img-responsive&quot; src=&quot;https://www.nostarch.com/sites/default/files/styles/uc_product/public/ArduinoInventor%27sGuide_cover_0.png?itok=eSSQoSei&quot; width=&quot;170&quot; height=&quot;225&quot; alt=&quot;The Arduino Inventor&amp;#039;s Guide&quot; title=&quot;The Arduino Inventor&amp;#039;s Guide&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;&lt;a href=&quot;/sparkfunarduino&quot;&gt;The Arduino Inventor&#039;s Guide&lt;/a&gt; is a hands-on introduction to exploring electronics with Arduino for total beginners. &lt;i&gt;Now Available in Early Access!&lt;/i&gt;&lt;/p&gt;
42
+ &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
43
+ <pubDate>Tue, 19 May 2015 23:41:36 +0000</pubDate>
44
+ <dc:creator>Anonymous</dc:creator>
45
+ <guid isPermaLink="false">346 at https://www.nostarch.com</guid>
46
+ </item>
47
+ <item>
48
+ <title>The Manga Guide to Microprocessors</title>
49
+ <link>https://www.nostarch.com/microprocessors</link>
50
+ <description>&lt;div class=&quot;field field-name-field-image-cache field-type-image field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;a href=&quot;/microprocessors&quot;&gt;&lt;img class=&quot;img-responsive&quot; src=&quot;https://www.nostarch.com/sites/default/files/styles/uc_product/public/MangaMicroprocessors_cover_0.png?itok=MxmdxH9e&quot; width=&quot;170&quot; height=&quot;225&quot; alt=&quot;The Manga Guide to Microprocessors&quot; title=&quot;The Manga Guide to Microprocessors&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;&lt;a href=&quot;/microprocessors&quot;&gt;The Manga Guide to Microprocessors&lt;/a&gt; is a comic guide to microprocessors, computer architecture, and digital operations. &lt;i&gt;Now Available in Early Access!&lt;/i&gt;&lt;/p&gt;
51
+ &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
52
+ <pubDate>Mon, 24 Oct 2016 20:43:55 +0000</pubDate>
53
+ <dc:creator>ahariri</dc:creator>
54
+ <guid isPermaLink="false">393 at https://www.nostarch.com</guid>
55
+ </item>
56
+ <item>
57
+ <title>Arduino Project Handbook, Vol. 2</title>
58
+ <link>https://www.nostarch.com/arduinohandbook2</link>
59
+ <description>&lt;div class=&quot;field field-name-field-image-cache field-type-image field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;a href=&quot;/arduinohandbook2&quot;&gt;&lt;img class=&quot;img-responsive&quot; src=&quot;https://www.nostarch.com/sites/default/files/styles/uc_product/public/ArduinoProjectHandbookII_cover.png?itok=FkF3sw-p&quot; width=&quot;170&quot; height=&quot;225&quot; alt=&quot;Arduino Project Handbook Vol. 2&quot; title=&quot;Arduino Project Handbook Vol. 2&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;&lt;a href=&quot;/arduinohandbook2&quot;&gt;Arduino Project Handbook, Vol. 2&lt;/a&gt; is a full-color guide to building 25 fun and practical projects with the low-cost Arduino microcontroller. &lt;i&gt;Now Available in Early Access!&lt;/i&gt;&lt;/p&gt;
60
+ &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
61
+ <pubDate>Mon, 24 Oct 2016 21:24:45 +0000</pubDate>
62
+ <dc:creator>ahariri</dc:creator>
63
+ <guid isPermaLink="false">394 at https://www.nostarch.com</guid>
64
+ </item>
65
+ <item>
66
+ <title>Think Like a Programmer, Python Edition</title>
67
+ <link>https://www.nostarch.com/thinkpython</link>
68
+ <description>&lt;div class=&quot;field field-name-field-image-cache field-type-image field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;a href=&quot;/thinkpython&quot;&gt;&lt;img class=&quot;img-responsive&quot; src=&quot;https://www.nostarch.com/sites/default/files/styles/uc_product/public/ThinkLikeaProgrammerPython_cover.png?itok=1yCoVJfA&quot; width=&quot;170&quot; height=&quot;225&quot; alt=&quot;Think Like a Programmer, Python Edition&quot; title=&quot;Think Like a Programmer, Python Edition&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;The Python edition of &lt;a href=&quot;https://www.nostarch.com/thinkpython&quot;&gt;Think Like a Programmer&lt;/a&gt; will teach you how to solve problems with programming.&lt;/p&gt;
69
+ &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
70
+ <pubDate>Fri, 10 Mar 2017 23:50:24 +0000</pubDate>
71
+ <dc:creator>ahariri</dc:creator>
72
+ <guid isPermaLink="false">399 at https://www.nostarch.com</guid>
73
+ </item>
74
+ <item>
75
+ <title>Computers for Seniors</title>
76
+ <link>https://www.nostarch.com/computersforseniors</link>
77
+ <description>&lt;div class=&quot;field field-name-field-image-cache field-type-image field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;a href=&quot;/computersforseniors&quot;&gt;&lt;img class=&quot;img-responsive&quot; src=&quot;https://www.nostarch.com/sites/default/files/styles/uc_product/public/ComputersforSeniors_cover.png?itok=sWPaSVC7&quot; width=&quot;170&quot; height=&quot;225&quot; alt=&quot;Computers for Seniors&quot; title=&quot;Computers for Seniors&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.nostarch.com/computersforseniors&quot;&gt;Computers for Seniors&lt;/a&gt; is a step-by-step guide to learning basic computer skills.&lt;/p&gt;
78
+ &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
79
+ <pubDate>Wed, 13 Apr 2016 20:07:07 +0000</pubDate>
80
+ <dc:creator>Caitlin Griffin</dc:creator>
81
+ <guid isPermaLink="false">375 at https://www.nostarch.com</guid>
82
+ </item>
83
+ <item>
84
+ <title>Game Console</title>
85
+ <link>https://www.nostarch.com/gameconsole</link>
86
+ <description>&lt;div class=&quot;field field-name-field-image-cache field-type-image field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;a href=&quot;/gameconsole&quot;&gt;&lt;img class=&quot;img-responsive&quot; src=&quot;https://www.nostarch.com/sites/default/files/styles/uc_product/public/gameconsole_cover.png?itok=S9JH4c1z&quot; width=&quot;176&quot; height=&quot;225&quot; alt=&quot;The Game Console&quot; title=&quot;The Game Console&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;&lt;b&gt;&lt;a href=&quot;/gameconsole&quot;&gt;The Game Console&lt;/a&gt;&lt;/b&gt; is a visual history of video game technology, with gorgeous photos of more than 80 video game consoles manufactured since 1972.&lt;/p&gt;
87
+ &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
88
+ <pubDate>Sat, 09 Jan 2016 00:31:43 +0000</pubDate>
89
+ <dc:creator>Caitlin Griffin</dc:creator>
90
+ <guid isPermaLink="false">362 at https://www.nostarch.com</guid>
91
+ </item>
92
+ <item>
93
+ <title>Serious Cryptography</title>
94
+ <link>https://www.nostarch.com/seriouscrypto</link>
95
+ <description>&lt;div class=&quot;field field-name-field-image-cache field-type-image field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;a href=&quot;/seriouscrypto&quot;&gt;&lt;img class=&quot;img-responsive&quot; src=&quot;https://www.nostarch.com/sites/default/files/styles/uc_product/public/SeriousCrypto_cover_0.png?itok=s0OdQ-Pl&quot; width=&quot;170&quot; height=&quot;225&quot; alt=&quot;Serious Cryptography&quot; title=&quot;Serious Cryptography&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.nostarch.com/SeriousCrypto&quot;&gt;Serious Cryptography&lt;/a&gt; is a practical guide to the past, present, and future of cryptographic systems and algorithms. &lt;i&gt;Now Available in Early Access!&lt;/i&gt;&lt;/p&gt;
96
+ &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
97
+ <pubDate>Tue, 25 Apr 2017 22:25:46 +0000</pubDate>
98
+ <dc:creator>Caitlin Griffin</dc:creator>
99
+ <guid isPermaLink="false">428 at https://www.nostarch.com</guid>
100
+ </item>
101
+ </channel>
102
+ </rss>
103
+
104
+ ---
105
+
106
+ feed.format: rss 2.0
107
+ feed.title: No Starch Press - Coming Soon
108
+ feed.url: https://www.nostarch.com/frontpage_comingsoon
109
+ feed.summary: Keep an eye on what's coming out down the line.
110
+
111
+
112
+ feed.items.size: >>> 10
113
+
114
+ feed.items[0].title: PoC||GTFO
115
+ feed.items[0].url: https://www.nostarch.com/gtfo
116
+ feed.items[0].guid: 423 at https://www.nostarch.com
117
+ feed.items[0].updated: >>> DateTime.new( 2017, 4, 11, 23, 50, 54 )
118
+
119
+ feed.items[1].title: Coding iPhone Apps for Kids
120
+ feed.items[1].url: https://www.nostarch.com/iphoneappsforkids
121
+ feed.items[1].guid: 371 at https://www.nostarch.com
122
+ feed.items[1].updated: >>> DateTime.new( 2016, 3, 11, 19, 7, 21 )
123
+
124
+
125
+ >>> pp feed.items[0].summary