planet 0.3.11 → 0.3.12

Sign up to get free protection for your applications and to get access to all the features.
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(config: conf.fetch('planet', {}))
54
-
55
- conf['blogs'].each do |blog|
56
- @planet.blogs << Planet::Blog.new(
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
 
@@ -1,5 +1,5 @@
1
- require 'feedzirra'
2
- require 'mustache'
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 = attributes.fetch(: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
@@ -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
@@ -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
@@ -1,8 +1,8 @@
1
- module Planet
1
+ class Planet
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 3
5
- PATCH = 11
5
+ PATCH = 12
6
6
 
7
7
  def self.to_s
8
8
  [MAJOR, MINOR, PATCH].join('.')
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.11
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-04 00:00:00.000000000 Z
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