planet 0.3.11 → 0.3.12
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.
- data/bin/planet +4 -13
- data/lib/planet.rb +13 -128
- data/lib/planet/blog.rb +79 -0
- data/lib/planet/parsers.rb +64 -0
- data/lib/planet/parsers/base_parser.rb +24 -0
- data/lib/planet/post.rb +58 -0
- data/lib/planet/version.rb +2 -2
- metadata +6 -2
data/bin/planet
CHANGED
@@ -50,19 +50,10 @@ command :generate do |c|
|
|
50
50
|
c.action do |global_options,options,args|
|
51
51
|
conf = YAML.load_file('planet.yml')
|
52
52
|
|
53
|
-
@planet = Planet.new(
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
feed: blog['feed'],
|
58
|
-
url: blog['url'],
|
59
|
-
author: blog['author'],
|
60
|
-
image: blog['image'],
|
61
|
-
posts: [],
|
62
|
-
planet: @planet,
|
63
|
-
twitter: blog['twitter']
|
64
|
-
)
|
65
|
-
end
|
53
|
+
@planet = Planet.new(
|
54
|
+
config: conf.fetch('planet', {}),
|
55
|
+
blogs: conf.fetch('blogs', [])
|
56
|
+
)
|
66
57
|
|
67
58
|
@planet.aggregate
|
68
59
|
|
data/lib/planet.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'planet/version'
|
2
|
+
require 'planet/blog'
|
3
3
|
|
4
4
|
class Planet
|
5
5
|
|
@@ -7,7 +7,17 @@ class Planet
|
|
7
7
|
|
8
8
|
def initialize(attributes = {})
|
9
9
|
self.config = attributes[:config]
|
10
|
-
self.blogs
|
10
|
+
self.blogs = attributes.fetch(:blogs, []).map do |blog|
|
11
|
+
Blog.new(
|
12
|
+
feed: blog['feed'],
|
13
|
+
url: blog['url'],
|
14
|
+
author: blog['author'],
|
15
|
+
image: blog['image'],
|
16
|
+
posts: [],
|
17
|
+
planet: self,
|
18
|
+
twitter: blog['twitter']
|
19
|
+
)
|
20
|
+
end
|
11
21
|
end
|
12
22
|
|
13
23
|
def posts
|
@@ -32,129 +42,4 @@ class Planet
|
|
32
42
|
File.open(file_name + '.markdown', "w+") { |f| f.write(post.to_s) }
|
33
43
|
end
|
34
44
|
end
|
35
|
-
|
36
|
-
class Post
|
37
|
-
|
38
|
-
attr_accessor :title, :content, :date, :url, :blog
|
39
|
-
|
40
|
-
def initialize(attributes = {})
|
41
|
-
self.title = attributes[:title]
|
42
|
-
self.content = attributes[:content]
|
43
|
-
self.date = attributes[:date]
|
44
|
-
self.url = attributes[:url]
|
45
|
-
self.blog = attributes[:blog]
|
46
|
-
end
|
47
|
-
|
48
|
-
def to_s
|
49
|
-
"#{ header }#{ content }#{ footer }"
|
50
|
-
end
|
51
|
-
|
52
|
-
def to_hash
|
53
|
-
{
|
54
|
-
post_content: self.content,
|
55
|
-
post_title: self.title,
|
56
|
-
post_date: self.date,
|
57
|
-
image_url: self.blog.image,
|
58
|
-
author: self.blog.author,
|
59
|
-
blog_url: self.blog.url,
|
60
|
-
blog_name: self.blog.name,
|
61
|
-
post_url: self.url,
|
62
|
-
twitter: self.blog.twitter,
|
63
|
-
twitter_url: "http://twitter.com/#{ self.blog.twitter }"
|
64
|
-
}
|
65
|
-
end
|
66
|
-
|
67
|
-
def header
|
68
|
-
## TODO: We need categories/tags
|
69
|
-
file = self.blog.planet.config.fetch('templates_directory', '_layouts/') + 'header.md'
|
70
|
-
file_contents = File.read(file)
|
71
|
-
|
72
|
-
Mustache.render(file_contents, self.to_hash)
|
73
|
-
end
|
74
|
-
|
75
|
-
def footer
|
76
|
-
file = self.blog.planet.config.fetch('templates_directory', '_layouts/') + 'author.html'
|
77
|
-
file_contents = File.read(file)
|
78
|
-
|
79
|
-
Mustache.render(file_contents, self.to_hash)
|
80
|
-
end
|
81
|
-
|
82
|
-
def file_name
|
83
|
-
name_date = date ? date.strftime('%Y-%m-%d') : nil
|
84
|
-
name_title = title.downcase.scan(/\w+/).join('-')
|
85
|
-
|
86
|
-
[name_date, name_title].join('-')
|
87
|
-
end
|
88
|
-
|
89
|
-
end
|
90
|
-
|
91
|
-
class Blog
|
92
|
-
|
93
|
-
attr_accessor :url, :feed, :name, :author, :image, :twitter, :posts, :planet
|
94
|
-
|
95
|
-
def initialize(attributes = {})
|
96
|
-
self.url = attributes[:url]
|
97
|
-
self.feed = attributes[:feed]
|
98
|
-
self.name = attributes[:name]
|
99
|
-
self.author = attributes[:author]
|
100
|
-
self.image = attributes[:image]
|
101
|
-
self.twitter = attributes[:twitter]
|
102
|
-
self.posts = attributes.fetch(:posts, [])
|
103
|
-
self.planet = attributes[:planet]
|
104
|
-
end
|
105
|
-
|
106
|
-
def fetch
|
107
|
-
feed = Feedzirra::Feed.fetch_and_parse(self.feed)
|
108
|
-
|
109
|
-
self.name ||= feed.title || 'the source'
|
110
|
-
self.url ||= feed.url
|
111
|
-
|
112
|
-
if self.url.nil?
|
113
|
-
abort "#{ self.author }'s blog does not have a url field on it's feed, you will need to specify it on planet.yml"
|
114
|
-
end
|
115
|
-
|
116
|
-
feed.entries.each do |entry|
|
117
|
-
## TODO: I should probably consider using feed 'adapters' for specific
|
118
|
-
## blog engine feeds that don't have their stuff on the standard fields.
|
119
|
-
## Example: blogspot has the content on "summary" instead of content ¬¬.
|
120
|
-
content = if !entry.content.nil?
|
121
|
-
self.sanitize_images(entry.content.strip)
|
122
|
-
elsif !entry.summary.nil?
|
123
|
-
self.sanitize_images(entry.summary.strip)
|
124
|
-
else
|
125
|
-
abort "=> No content found on entry"
|
126
|
-
end
|
127
|
-
|
128
|
-
title = if !entry.title.nil?
|
129
|
-
entry.title.sanitize
|
130
|
-
else
|
131
|
-
self.name
|
132
|
-
end
|
133
|
-
|
134
|
-
self.posts << @post = Post.new(
|
135
|
-
title: title,
|
136
|
-
content: content,
|
137
|
-
date: entry.published,
|
138
|
-
url: self.url + entry.url,
|
139
|
-
blog: self
|
140
|
-
)
|
141
|
-
|
142
|
-
puts "=> Found post titled #{ @post.title } - by #{ @post.blog.author }"
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def sanitize_images(html)
|
147
|
-
## We take all images with src not matching http refs and append
|
148
|
-
## the original blog to them.
|
149
|
-
html.scan(/<img src="([^h"]+)"/).flatten.each do |img|
|
150
|
-
if img[0] == '/'
|
151
|
-
html.gsub!(img, "#{ self.url }#{ img }")
|
152
|
-
else
|
153
|
-
html.gsub!(img, "#{ self.url }/#{ img }")
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
html
|
158
|
-
end
|
159
|
-
end
|
160
45
|
end
|
data/lib/planet/blog.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'planet/post'
|
2
|
+
require 'planet/parsers'
|
3
|
+
|
4
|
+
class Planet
|
5
|
+
class Blog
|
6
|
+
|
7
|
+
attr_accessor :url, :feed, :type, :name, :author, :image, :twitter, :posts, :planet
|
8
|
+
|
9
|
+
def initialize(attributes = {})
|
10
|
+
self.url = attributes[:url]
|
11
|
+
self.feed = attributes[:feed]
|
12
|
+
self.type = attributes[:type]
|
13
|
+
self.name = attributes[:name]
|
14
|
+
self.author = attributes[:author]
|
15
|
+
self.image = attributes[:image]
|
16
|
+
self.twitter = attributes[:twitter]
|
17
|
+
self.posts = attributes.fetch(:posts, [])
|
18
|
+
self.planet = attributes[:planet]
|
19
|
+
|
20
|
+
# get parser-manager instance
|
21
|
+
@parsers = Parsers.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch
|
25
|
+
# given parser can be set arbitrarily with :type or inferred from the domain
|
26
|
+
parser = self.type ? @parsers.get_parser(self.type) : @parsers.get_parser_for(self.feed)
|
27
|
+
|
28
|
+
# parser instances should mimick Feedzirra interface
|
29
|
+
feed = parser.fetch_and_parse(self.feed)
|
30
|
+
|
31
|
+
self.name ||= feed.title || 'the source'
|
32
|
+
self.url ||= feed.url
|
33
|
+
|
34
|
+
if self.url.nil?
|
35
|
+
abort "#{ self.author }'s blog does not have a url field on it's feed, you will need to specify it on planet.yml"
|
36
|
+
end
|
37
|
+
|
38
|
+
feed.entries.each do |entry|
|
39
|
+
content = if !entry.content.nil?
|
40
|
+
self.sanitize_images(entry.content.strip)
|
41
|
+
elsif !entry.summary.nil?
|
42
|
+
self.sanitize_images(entry.summary.strip)
|
43
|
+
else
|
44
|
+
abort "=> No content found on entry"
|
45
|
+
end
|
46
|
+
|
47
|
+
title = if !entry.title.nil?
|
48
|
+
entry.title.sanitize
|
49
|
+
else
|
50
|
+
self.name
|
51
|
+
end
|
52
|
+
|
53
|
+
self.posts << @post = Post.new(
|
54
|
+
title: title,
|
55
|
+
content: content,
|
56
|
+
date: entry.published,
|
57
|
+
url: self.url + entry.url,
|
58
|
+
blog: self
|
59
|
+
)
|
60
|
+
|
61
|
+
puts "=> Found post titled #{ @post.title } - by #{ @post.blog.author }"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def sanitize_images(html)
|
66
|
+
## We take all images with src not matching http refs and append
|
67
|
+
## the original blog to them.
|
68
|
+
html.scan(/<img src="([^h"]+)"/).flatten.each do |img|
|
69
|
+
if img[0] == '/'
|
70
|
+
html.gsub!(img, "#{ self.url }#{ img }")
|
71
|
+
else
|
72
|
+
html.gsub!(img, "#{ self.url }/#{ img }")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
html
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'feedzirra'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
class Planet
|
5
|
+
# Parsers class - manager for the feed parsers
|
6
|
+
#
|
7
|
+
# parser classes inherit from Planet::Parsers::BaseParser
|
8
|
+
# and are added automatically to the list of available parsers.
|
9
|
+
# files located on planet/parsers are automatically loaded.
|
10
|
+
class Parsers
|
11
|
+
@@parsers = Set.new
|
12
|
+
|
13
|
+
def self.add_parser(parser)
|
14
|
+
@@parsers << parser
|
15
|
+
end
|
16
|
+
|
17
|
+
# Parser instances keep indexes of the available parsers and
|
18
|
+
# check for duplicate definitions (need to use an instance
|
19
|
+
# because #inherited gets called as soon as the class is seen
|
20
|
+
# but before it is fully defined).
|
21
|
+
def initialize
|
22
|
+
@types, @domains = {}, {}
|
23
|
+
|
24
|
+
@@parsers.each do |parser|
|
25
|
+
new_type, new_domains = parser.type, parser.domains
|
26
|
+
|
27
|
+
fail("duplicate type") if new_type and @types.has_key? new_type
|
28
|
+
fail("overlapping domains") unless (@domains.keys & new_domains).empty?
|
29
|
+
|
30
|
+
@types[new_type] = parser if new_type
|
31
|
+
new_domains.each do |new_domain|
|
32
|
+
@domains[new_domain] = parser
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# returns the appropiate parser based on the type
|
38
|
+
def get_parser(type)
|
39
|
+
begin
|
40
|
+
return @types.fetch(type)
|
41
|
+
rescue KeyError => e
|
42
|
+
raise(ArgumentError, "No parser for type '#{ type }'", caller)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# returns any parser that can handle this feeds' domain,
|
47
|
+
# defaults to Feedzirra if none available.
|
48
|
+
def get_parser_for(feed)
|
49
|
+
feed_domain = URI(feed).host
|
50
|
+
|
51
|
+
@domains.each do |domain, parser|
|
52
|
+
return parser if feed_domain.end_with? domain
|
53
|
+
end
|
54
|
+
|
55
|
+
return Feedzirra::Feed # default generic parser
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# load parsers
|
61
|
+
dirname = File.join([File.dirname(__FILE__), 'parsers'])
|
62
|
+
Dir.open(dirname).each do |filename|
|
63
|
+
require "#{dirname}/#{filename}" if filename.end_with? '.rb'
|
64
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Planet
|
2
|
+
class Parsers
|
3
|
+
# base class for feed parsers
|
4
|
+
# subclasses should declare @type and @domains
|
5
|
+
# and also mimick Feedzirra interface.
|
6
|
+
class BaseParser
|
7
|
+
def self.type
|
8
|
+
@type
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.domains
|
12
|
+
@domains || []
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.inherited(parser)
|
16
|
+
Parsers.add_parser parser
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.fetch_and_parse(feed)
|
20
|
+
raise(Exception, "Not implemented", caller)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/planet/post.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'mustache'
|
2
|
+
|
3
|
+
class Planet
|
4
|
+
class Post
|
5
|
+
|
6
|
+
attr_accessor :title, :content, :date, :url, :blog
|
7
|
+
|
8
|
+
def initialize(attributes = {})
|
9
|
+
self.title = attributes[:title]
|
10
|
+
self.content = attributes[:content]
|
11
|
+
self.date = attributes[:date]
|
12
|
+
self.url = attributes[:url]
|
13
|
+
self.blog = attributes[:blog]
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
"#{ header }#{ content }#{ footer }"
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hash
|
21
|
+
{
|
22
|
+
post_content: self.content,
|
23
|
+
post_title: self.title,
|
24
|
+
post_date: self.date,
|
25
|
+
image_url: self.blog.image,
|
26
|
+
author: self.blog.author,
|
27
|
+
blog_url: self.blog.url,
|
28
|
+
blog_name: self.blog.name,
|
29
|
+
post_url: self.url,
|
30
|
+
twitter: self.blog.twitter,
|
31
|
+
twitter_url: "http://twitter.com/#{ self.blog.twitter }"
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def header
|
36
|
+
## TODO: We need categories/tags
|
37
|
+
file = self.blog.planet.config.fetch('templates_directory', '_layouts/') + 'header.md'
|
38
|
+
file_contents = File.read(file)
|
39
|
+
|
40
|
+
Mustache.render(file_contents, self.to_hash)
|
41
|
+
end
|
42
|
+
|
43
|
+
def footer
|
44
|
+
file = self.blog.planet.config.fetch('templates_directory', '_layouts/') + 'author.html'
|
45
|
+
file_contents = File.read(file)
|
46
|
+
|
47
|
+
Mustache.render(file_contents, self.to_hash)
|
48
|
+
end
|
49
|
+
|
50
|
+
def file_name
|
51
|
+
name_date = date ? date.strftime('%Y-%m-%d') : nil
|
52
|
+
name_title = title.downcase.scan(/\w+/).join('-')
|
53
|
+
|
54
|
+
[name_date, name_title].join('-')
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
data/lib/planet/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: planet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.12
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -83,6 +83,10 @@ extensions: []
|
|
83
83
|
extra_rdoc_files: []
|
84
84
|
files:
|
85
85
|
- bin/planet
|
86
|
+
- lib/planet/blog.rb
|
87
|
+
- lib/planet/parsers/base_parser.rb
|
88
|
+
- lib/planet/parsers.rb
|
89
|
+
- lib/planet/post.rb
|
86
90
|
- lib/planet/version.rb
|
87
91
|
- lib/planet.rb
|
88
92
|
homepage: http://poteland.com
|