feedtxt 0.0.1 → 0.1.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.
- checksums.yaml +4 -4
- data/Manifest.txt +6 -0
- data/README.md +191 -1
- data/lib/feedtxt.rb +1 -0
- data/lib/feedtxt/parser.rb +91 -0
- data/lib/feedtxt/version.rb +2 -2
- data/test/feeds/spec/example.yaml.txt +15 -0
- data/test/feeds/spec/podcast.yaml.txt +26 -0
- data/test/helper.rb +23 -0
- data/test/test_version.rb +19 -0
- data/test/test_yaml.rb +72 -0
- metadata +7 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 08b6aa5f6093d99cc013764f924c5a1845b8b3f0
|
4
|
+
data.tar.gz: ae29161c8ffab020ca196d58dd55cdd0f823308c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa7f505525df6950631f8789b44a52d8216573e52b32e6f452f6448c561adc4339ba5f760101e5451543945722d06bff9392655023d62c6242e7507772787814
|
7
|
+
data.tar.gz: 49dfd5b5c1f2747b47c0ef45bdb9580cae8ea745c0a3bc75fce88d5108415b15f92601d5b629bed7fbf75b4cc7bb8901ef1fad1c68e7a11b25c30b88eb3c35fd
|
data/Manifest.txt
CHANGED
data/README.md
CHANGED
@@ -10,7 +10,197 @@
|
|
10
10
|
|
11
11
|
## Usage
|
12
12
|
|
13
|
-
|
13
|
+
Use `Feedtxt::Parser.parse` to read / parse feeds in text (using the Feed.TXT)
|
14
|
+
format.
|
15
|
+
The parse method will return an array:
|
16
|
+
|
17
|
+
```
|
18
|
+
[ feed_metadata,
|
19
|
+
[
|
20
|
+
[ item_metadata, item_content ],
|
21
|
+
[ item_metadata, item_content ],
|
22
|
+
...
|
23
|
+
]
|
24
|
+
]
|
25
|
+
```
|
26
|
+
|
27
|
+
- The 1st element is the feed metadata hash.
|
28
|
+
- The 2nd element is the items array.
|
29
|
+
- The 1st element in an item array is the item metadata hash.
|
30
|
+
- The 2nd element in an item array is the item content.
|
31
|
+
|
32
|
+
Easier to see it in action. Let's read in:
|
33
|
+
|
34
|
+
``` ruby
|
35
|
+
require 'feedtxt'
|
36
|
+
|
37
|
+
text =<<TXT
|
38
|
+
|>>>
|
39
|
+
title: "My Example Feed"
|
40
|
+
home_page_url: "https://example.org/"
|
41
|
+
feed_url: "https://example.org/feed.txt"
|
42
|
+
</>
|
43
|
+
id: "2"
|
44
|
+
url: "https://example.org/second-item"
|
45
|
+
---
|
46
|
+
This is a second item.
|
47
|
+
</>
|
48
|
+
id: "1"
|
49
|
+
url: "https://example.org/initial-post"
|
50
|
+
---
|
51
|
+
Hello, world!
|
52
|
+
<<<|
|
53
|
+
TXT
|
54
|
+
|
55
|
+
feed = Feedtxt::Parser.parse( text )
|
56
|
+
pp feed
|
57
|
+
```
|
58
|
+
|
59
|
+
resulting in:
|
60
|
+
|
61
|
+
``` ruby
|
62
|
+
[
|
63
|
+
{"title" =>"My Example Feed",
|
64
|
+
"home_page_url"=>"https://example.org/",
|
65
|
+
"feed_url" =>"https://example.org/feed.txt"
|
66
|
+
},
|
67
|
+
[[
|
68
|
+
{"id" =>"2",
|
69
|
+
"url"=>"https://example.org/second-item"
|
70
|
+
},
|
71
|
+
"This is a second item."
|
72
|
+
],
|
73
|
+
[
|
74
|
+
{"id"=>"1",
|
75
|
+
"url"=>"https://example.org/initial-post"
|
76
|
+
},
|
77
|
+
"Hello, world!"
|
78
|
+
]]
|
79
|
+
]
|
80
|
+
```
|
81
|
+
|
82
|
+
and use like:
|
83
|
+
|
84
|
+
``` ruby
|
85
|
+
|
86
|
+
feed_metadata = feed[0]
|
87
|
+
feed_items = feed[1]
|
88
|
+
|
89
|
+
feed_metadata[ 'title' ]
|
90
|
+
# => "My Example Feed"
|
91
|
+
feed_metadata[ 'feed_url' ]
|
92
|
+
# => "https://example.org/feed.txt"
|
93
|
+
|
94
|
+
item = feed_items[0] # or feed[1][0]
|
95
|
+
item_metadata = item[0] # or feed[1][0][0]
|
96
|
+
item_content = item[1] # or feed[1][0][1]
|
97
|
+
|
98
|
+
item_metadata[ 'id' ]
|
99
|
+
# => "2"
|
100
|
+
item_metadata[ 'url' ]
|
101
|
+
# => "https://example.org/second-item"
|
102
|
+
item_content
|
103
|
+
# => "This is a second item."
|
104
|
+
|
105
|
+
item = feed_items[1] # or feed[1][1]
|
106
|
+
item_metadata = item[0] # or feed[1][1][0]
|
107
|
+
item_content = item[1] # or feed[1][1][1]
|
108
|
+
|
109
|
+
item_metadata[ 'id' ]
|
110
|
+
# => "1"
|
111
|
+
item_metadata[ 'url' ]
|
112
|
+
# => "https://example.org/initial-post"
|
113
|
+
item_content
|
114
|
+
# => "Hello, world!"
|
115
|
+
...
|
116
|
+
```
|
117
|
+
|
118
|
+
Another example. Let's try a podcast:
|
119
|
+
|
120
|
+
|
121
|
+
``` ruby
|
122
|
+
text =<<TXT
|
123
|
+
|>>>
|
124
|
+
comment: "This is a podcast feed. You can add..."
|
125
|
+
title: "The Record"
|
126
|
+
home_page_url: "http://therecord.co/"
|
127
|
+
feed_url: "http://therecord.co/feed.txt"
|
128
|
+
</>
|
129
|
+
id: "http://therecord.co/chris-parrish"
|
130
|
+
title: "Special #1 - Chris Parrish"
|
131
|
+
url: "http://therecord.co/chris-parrish"
|
132
|
+
summary: "Brent interviews Chris Parrish, co-host of The Record and one-half of Aged & Distilled."
|
133
|
+
published: 2014-05-09T14:04:00-07:00
|
134
|
+
attachments:
|
135
|
+
- url: "http://therecord.co/downloads/The-Record-sp1e1-ChrisParrish.m4a"
|
136
|
+
mime_type: "audio/x-m4a"
|
137
|
+
size_in_bytes: 89970236
|
138
|
+
duration_in_seconds: 6629
|
139
|
+
---
|
140
|
+
Chris has worked at [Adobe][1] and as a founder of Rogue Sheep, which won an Apple Design Award for Postage.
|
141
|
+
Chris's new company is Aged & Distilled with Guy English - which shipped [Napkin](2),
|
142
|
+
a Mac app for visual collaboration. Chris is also the co-host of The Record.
|
143
|
+
He lives on [Bainbridge Island][3], a quick ferry ride from Seattle.
|
144
|
+
|
145
|
+
[1]: http://adobe.com/
|
146
|
+
[2]: http://aged-and-distilled.com/napkin/
|
147
|
+
[3]: http://www.ci.bainbridge-isl.wa.us/
|
148
|
+
<<<|
|
149
|
+
TXT
|
150
|
+
|
151
|
+
feed = Feedtxt::Parser.parse( text )
|
152
|
+
pp feed
|
153
|
+
```
|
154
|
+
|
155
|
+
resulting in:
|
156
|
+
|
157
|
+
``` ruby
|
158
|
+
[{"comment"=>"This is a podcast feed. You can add...",
|
159
|
+
"title"=>"The Record",
|
160
|
+
"home_page_url"=>"http://therecord.co/",
|
161
|
+
"feed_url"=>"http://therecord.co/feed.txt"
|
162
|
+
},
|
163
|
+
[
|
164
|
+
[{"id"=>"http://therecord.co/chris-parrish",
|
165
|
+
"title"=>"Special #1 - Chris Parrish",
|
166
|
+
"url"=>"http://therecord.co/chris-parrish",
|
167
|
+
"summary"=>"Brent interviews Chris Parrish, co-host of The Record and...",
|
168
|
+
"published"=>2014-05-09 23:04:00 +0200,
|
169
|
+
"attachments"=>
|
170
|
+
[{"url"=>"http://therecord.co/downloads/The-Record-sp1e1-ChrisParrish.m4a",
|
171
|
+
"mime_type"=>"audio/x-m4a",
|
172
|
+
"size_in_bytes"=>89970236,
|
173
|
+
"duration_in_seconds"=>6629}]
|
174
|
+
},
|
175
|
+
"Chris has worked at [Adobe][1] and as a founder of Rogue Sheep..."
|
176
|
+
]
|
177
|
+
]
|
178
|
+
]
|
179
|
+
```
|
180
|
+
|
181
|
+
and use like:
|
182
|
+
|
183
|
+
``` ruby
|
184
|
+
feed_metadata = feed[0]
|
185
|
+
feed_items = feed[1]
|
186
|
+
|
187
|
+
feed_metadata[ 'title' ]
|
188
|
+
# => "The Record"
|
189
|
+
feed_metadata[ 'feed_url' ]
|
190
|
+
# => "http://therecord.co/feed.txt"
|
191
|
+
|
192
|
+
item = feed_items[0] # or feed[1][0]
|
193
|
+
item_metadata = item[0] # or feed[1][0][0]
|
194
|
+
item_content = item[1] # or feed[1][0][1]
|
195
|
+
|
196
|
+
item_metadata[ 'title' ]
|
197
|
+
# => "Special #1 - Chris Parrish"
|
198
|
+
item_metadata[ 'url' ]
|
199
|
+
# => "http://therecord.co/chris-parrish
|
200
|
+
item_content
|
201
|
+
# => "Chris has worked at [Adobe][1] and as a founder of Rogue Sheep..."
|
202
|
+
...
|
203
|
+
```
|
14
204
|
|
15
205
|
|
16
206
|
## License
|
data/lib/feedtxt.rb
CHANGED
@@ -0,0 +1,91 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Feedtxt
|
4
|
+
|
5
|
+
|
6
|
+
class Parser
|
7
|
+
|
8
|
+
include LogUtils::Logging
|
9
|
+
|
10
|
+
|
11
|
+
### convenience class/factory method
|
12
|
+
def self.parse( text, opts={} )
|
13
|
+
self.new( text ).parse
|
14
|
+
end
|
15
|
+
|
16
|
+
### Note: lets keep/use same API as RSS::Parser for now
|
17
|
+
def initialize( text )
|
18
|
+
@text = text
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
FEED_BEGIN_RX = %r{^\|>>>$}
|
24
|
+
FEED_END_RX = %r{^<<<\|$}
|
25
|
+
FEED_NEXT_RX = %r{^</>$} ## pass 1: split/break up blocks
|
26
|
+
FEED_META_RX = %r{^---$} ## pass 2: break up item into metadata and content block
|
27
|
+
|
28
|
+
|
29
|
+
def parse
|
30
|
+
|
31
|
+
## find start marker e.g. |>>>
|
32
|
+
## todo: use regex - allow three or more >>>>>> or <<<<<<
|
33
|
+
## todo: allow spaces before and after
|
34
|
+
|
35
|
+
|
36
|
+
## todo/fix:
|
37
|
+
## use index-like finder return posbeg and posend!!!
|
38
|
+
## regex is not fixed length/width; we need to know the length
|
39
|
+
## check what is the best way? use regex match or something???
|
40
|
+
|
41
|
+
posbeg = @text.index( FEED_BEGIN_RX )
|
42
|
+
if posbeg.nil?
|
43
|
+
## nothing found return empty array for now; return nil - why? why not?
|
44
|
+
puts "warn !!! no begin marker found e.g. |>>>"
|
45
|
+
return []
|
46
|
+
end
|
47
|
+
|
48
|
+
posend = @text.index( FEED_END_RX, posbeg )
|
49
|
+
if posend.nil?
|
50
|
+
## nothing found return empty array for now; return nil - why? why not?
|
51
|
+
puts "warn !!! no end marker found e.g. <<<|"
|
52
|
+
return []
|
53
|
+
end
|
54
|
+
|
55
|
+
## cutoff - get text between begin and end marker
|
56
|
+
buf = @text[ posbeg+4...posend ].strip
|
57
|
+
## pp buf
|
58
|
+
|
59
|
+
####
|
60
|
+
## pass 1: split blocks by </>
|
61
|
+
### todo: allow <<<</>>>>
|
62
|
+
|
63
|
+
blocks = buf.split( FEED_NEXT_RX )
|
64
|
+
## pp blocks
|
65
|
+
|
66
|
+
## 1st block is feed meta data
|
67
|
+
block1st = blocks.shift ## get/remove 1st block from blocks
|
68
|
+
feed_metadata = YAML.load( block1st.strip )
|
69
|
+
|
70
|
+
feed_items = []
|
71
|
+
blocks.each do |block|
|
72
|
+
### note: do NOT use split e.g.--- is used by markdown
|
73
|
+
## only search for first --- to split (all others get ignored)
|
74
|
+
## todo: make three dashes --- (3) not hard-coded (allow more)
|
75
|
+
posmeta = block.index( FEED_META_RX )
|
76
|
+
item = []
|
77
|
+
item[0] = block[0...posmeta].strip
|
78
|
+
item[1] = block[posmeta+3..-1].strip
|
79
|
+
|
80
|
+
item_metadata = YAML.load( item[0] )
|
81
|
+
item_content = item[1]
|
82
|
+
|
83
|
+
feed_items << [item_metadata, item_content]
|
84
|
+
end
|
85
|
+
|
86
|
+
[ feed_metadata, feed_items ]
|
87
|
+
end # method parse
|
88
|
+
|
89
|
+
|
90
|
+
end # class Parser
|
91
|
+
end # module Feedtxt
|
data/lib/feedtxt/version.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
|>>>
|
2
|
+
title: "My Example Feed"
|
3
|
+
home_page_url: "https://example.org/"
|
4
|
+
feed_url: "https://example.org/feed.txt"
|
5
|
+
</>
|
6
|
+
id: "2"
|
7
|
+
url: "https://example.org/second-item"
|
8
|
+
---
|
9
|
+
This is a second item.
|
10
|
+
</>
|
11
|
+
id: "1"
|
12
|
+
url: "https://example.org/initial-post"
|
13
|
+
---
|
14
|
+
Hello, world!
|
15
|
+
<<<|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|>>>
|
2
|
+
comment: "This is a podcast feed. You can add this feed to your podcast client using the following URL: http://therecord.co/feed.json"
|
3
|
+
title: "The Record"
|
4
|
+
home_page_url: "http://therecord.co/"
|
5
|
+
feed_url: "http://therecord.co/feed.txt"
|
6
|
+
</>
|
7
|
+
id: "http://therecord.co/chris-parrish"
|
8
|
+
title: "Special #1 - Chris Parrish"
|
9
|
+
url: "http://therecord.co/chris-parrish"
|
10
|
+
summary: "Brent interviews Chris Parrish, co-host of The Record and one-half of Aged & Distilled."
|
11
|
+
published: 2014-05-09T14:04:00-07:00
|
12
|
+
attachments:
|
13
|
+
- url: "http://therecord.co/downloads/The-Record-sp1e1-ChrisParrish.m4a"
|
14
|
+
mime_type: "audio/x-m4a"
|
15
|
+
size_in_bytes: 89970236
|
16
|
+
duration_in_seconds: 6629
|
17
|
+
---
|
18
|
+
Chris has worked at [Adobe][1] and as a founder of Rogue Sheep, which won an Apple Design Award for Postage.
|
19
|
+
Chris's new company is Aged & Distilled with Guy English - which shipped [Napkin](2),
|
20
|
+
a Mac app for visual collaboration. Chris is also the co-host of The Record.
|
21
|
+
He lives on [Bainbridge Island][3], a quick ferry ride from Seattle.
|
22
|
+
|
23
|
+
[1]: http://adobe.com/
|
24
|
+
[2]: http://aged-and-distilled.com/napkin/
|
25
|
+
[3]: http://www.ci.bainbridge-isl.wa.us/
|
26
|
+
<<<|
|
data/test/helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
## $:.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
|
4
|
+
## minitest setup
|
5
|
+
|
6
|
+
require 'minitest/autorun'
|
7
|
+
|
8
|
+
require 'logutils'
|
9
|
+
require 'textutils'
|
10
|
+
|
11
|
+
|
12
|
+
## our own code
|
13
|
+
require 'feedtxt'
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
LogUtils::Logger.root.level = :debug
|
18
|
+
|
19
|
+
|
20
|
+
def read_text( name )
|
21
|
+
text = File.read( "#{Feedtxt.root}/test/feeds/#{name}.txt" )
|
22
|
+
text
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
###
|
2
|
+
# to run use
|
3
|
+
# ruby -I ./lib -I ./test test/test_version.rb
|
4
|
+
# or better
|
5
|
+
# rake test
|
6
|
+
|
7
|
+
require 'helper'
|
8
|
+
|
9
|
+
|
10
|
+
class TestVersion < MiniTest::Test
|
11
|
+
|
12
|
+
def test_version
|
13
|
+
|
14
|
+
puts "Feedtxt: #{Feedtxt::VERSION}"
|
15
|
+
|
16
|
+
assert true
|
17
|
+
end
|
18
|
+
|
19
|
+
end # class TestVersion
|
data/test/test_yaml.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
###
|
2
|
+
# to run use
|
3
|
+
# ruby -I ./lib -I ./test test/test_yaml.rb
|
4
|
+
# or better
|
5
|
+
# rake test
|
6
|
+
|
7
|
+
require 'helper'
|
8
|
+
|
9
|
+
|
10
|
+
class TestYaml < MiniTest::Test
|
11
|
+
|
12
|
+
def test_example
|
13
|
+
|
14
|
+
text = read_text( 'spec/example.yaml' )
|
15
|
+
pp text
|
16
|
+
|
17
|
+
feed = Feedtxt::Parser.parse( text )
|
18
|
+
pp feed
|
19
|
+
|
20
|
+
## pp feed.to_json
|
21
|
+
|
22
|
+
exp = [
|
23
|
+
{"title"=>"My Example Feed",
|
24
|
+
"home_page_url"=>"https://example.org/",
|
25
|
+
"feed_url"=>"https://example.org/feed.txt"},
|
26
|
+
[[
|
27
|
+
{"id"=>"2", "url"=>"https://example.org/second-item"},
|
28
|
+
"This is a second item."
|
29
|
+
],
|
30
|
+
[
|
31
|
+
{"id"=>"1", "url"=>"https://example.org/initial-post"},
|
32
|
+
"Hello, world!"
|
33
|
+
]]]
|
34
|
+
|
35
|
+
|
36
|
+
assert_equal exp, feed
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_podcast
|
40
|
+
|
41
|
+
text = read_text( 'spec/podcast.yaml' )
|
42
|
+
pp text
|
43
|
+
|
44
|
+
feed = Feedtxt::Parser.parse( text )
|
45
|
+
pp feed
|
46
|
+
|
47
|
+
## pp feed.to_json
|
48
|
+
|
49
|
+
exp =[{"comment"=>
|
50
|
+
"This is a podcast feed. You can add this feed to your podcast client using the following URL: http://therecord.co/feed.json",
|
51
|
+
"title"=>"The Record",
|
52
|
+
"home_page_url"=>"http://therecord.co/",
|
53
|
+
"feed_url"=>"http://therecord.co/feed.txt"},
|
54
|
+
[[{"id"=>"http://therecord.co/chris-parrish",
|
55
|
+
"title"=>"Special #1 - Chris Parrish",
|
56
|
+
"url"=>"http://therecord.co/chris-parrish",
|
57
|
+
"summary"=>
|
58
|
+
"Brent interviews Chris Parrish, co-host of The Record and one-half of Aged & Distilled.",
|
59
|
+
"published"=>DateTime.new( 2014, 5, 9, 23, 4, 0, '+02'),
|
60
|
+
"attachments"=>
|
61
|
+
[{"url"=>
|
62
|
+
"http://therecord.co/downloads/The-Record-sp1e1-ChrisParrish.m4a",
|
63
|
+
"mime_type"=>"audio/x-m4a",
|
64
|
+
"size_in_bytes"=>89970236,
|
65
|
+
"duration_in_seconds"=>6629}]},
|
66
|
+
"Chris has worked at [Adobe][1] and as a founder of Rogue Sheep, which won an Apple Design Award for Postage.\nChris's new company is Aged & Distilled with Guy English - which shipped [Napkin](2),\na Mac app for visual collaboration. Chris is also the co-host of The Record.\nHe lives on [Bainbridge Island][3], a quick ferry ride from Seattle.\n\n[1]: http://adobe.com/\n[2]: http://aged-and-distilled.com/napkin/\n[3]: http://www.ci.bainbridge-isl.wa.us/"]]]
|
67
|
+
|
68
|
+
assert_equal exp, feed
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
end # class TestYaml
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feedtxt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
@@ -53,7 +53,13 @@ files:
|
|
53
53
|
- README.md
|
54
54
|
- Rakefile
|
55
55
|
- lib/feedtxt.rb
|
56
|
+
- lib/feedtxt/parser.rb
|
56
57
|
- lib/feedtxt/version.rb
|
58
|
+
- test/feeds/spec/example.yaml.txt
|
59
|
+
- test/feeds/spec/podcast.yaml.txt
|
60
|
+
- test/helper.rb
|
61
|
+
- test/test_version.rb
|
62
|
+
- test/test_yaml.rb
|
57
63
|
homepage: https://github.com/feedtxt/feedtxt
|
58
64
|
licenses:
|
59
65
|
- Public Domain
|