feedparser 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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