twog 0.3.0 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,32 +9,42 @@ require 'bitly'
9
9
 
10
10
  # internal requires
11
11
  require 'twog/rss_parser'
12
+ require 'twog/rss_entry_to_twog_post_mapper'
12
13
  require 'twog/blog_posts_handler'
13
14
  require 'twog/twitter_handler'
15
+ require 'twog/post'
14
16
 
15
17
 
16
- class Twog
17
- extend RssParser
18
- extend BlogPostsHandler
19
- extend TwitterHandler
18
+ module Twog
19
+ module Twog
20
+ include RssParser
21
+ include RssEntryToTwogPostMapper
22
+ include BlogPostsHandler
23
+ include TwitterHandler
20
24
 
21
- def self.run(conf)
22
- posts = parse(conf['rss_feed'])
23
- posts = get_new_blog_posts(posts, conf['last_blog_post_tweeted'])
24
- return unless posts && posts.length > 0
25
- bitly = get_bitly_from(conf)
26
- tweet(posts, conf, bitly)
27
- end
25
+ def run(conf)
26
+ posts = get_posts_to_tweet(conf)
27
+ return unless posts && posts.length > 0
28
+ bitly = get_bitly_from(conf)
29
+ tweet(posts, conf, bitly)
30
+ end
28
31
 
29
- def self.get_bitly_from(conf)
30
- bitly_username = conf['bitly_username']
31
- bitly_api_key = conf['bitly_api_key']
32
- return nil unless (bitly_username && bitly_api_key)
33
- Bitly.new(bitly_username, bitly_api_key)
34
- end
35
-
36
- def self.version
37
- yml = YAML.load(File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION.yml])))
38
- "#{yml[:major]}.#{yml[:minor]}.#{yml[:patch]}"
32
+ def get_posts_to_tweet(conf)
33
+ posts = parse_feed(conf['rss_feed'])
34
+ posts = map(posts)
35
+ posts = get_new_blog_posts(posts, conf['last_blog_post_tweeted'])
36
+ end
37
+
38
+ def get_bitly_from(conf)
39
+ bitly_username = conf['bitly_username']
40
+ bitly_api_key = conf['bitly_api_key']
41
+ return nil unless (bitly_username && bitly_api_key)
42
+ Bitly.new(bitly_username, bitly_api_key)
43
+ end
44
+
45
+ def version
46
+ yml = YAML.load(File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION.yml])))
47
+ "#{yml[:major]}.#{yml[:minor]}.#{yml[:patch]}"
48
+ end
39
49
  end
40
50
  end
@@ -1,9 +1,11 @@
1
-
2
-
3
- module BlogPostsHandler
4
- def get_new_blog_posts(posts, last_blog_post_tweeted)
5
- return [] unless posts && posts.length > 0
6
- return posts unless last_blog_post_tweeted
7
- new_posts = posts.reject { |post| Time.parse(post.updated.content.to_s) <= Time.parse(last_blog_post_tweeted.to_s) }
1
+ module Twog
2
+ module BlogPostsHandler
3
+ def get_new_blog_posts(posts, last_blog_post_tweeted)
4
+ return [] unless posts && posts.length > 0
5
+ return posts unless last_blog_post_tweeted
6
+ new_posts = posts.reject do |post|
7
+ Time.parse(post.date.to_s) <= Time.parse(last_blog_post_tweeted.to_s)
8
+ end
9
+ end
8
10
  end
9
11
  end
@@ -0,0 +1,23 @@
1
+ module Twog
2
+ class Post
3
+ def initialize(post)
4
+ @post = post
5
+ end
6
+
7
+ def link
8
+ @post.link.respond_to?('href') ? @post.link.href : @post.link
9
+ end
10
+
11
+ def date
12
+ @post.respond_to?('updated') ? @post.updated.content : @post.pubDate
13
+ end
14
+
15
+ def title
16
+ @post.title.respond_to?('content') ? @post.title.content : @post.title
17
+ end
18
+
19
+ def <=>(other_post)
20
+ Time.parse(self.date.to_s) <=> Time.parse(other_post.date.to_s)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ module Twog
2
+ module RssEntryToTwogPostMapper
3
+ def map(rss_entries)
4
+ rss_entries.collect { |x| Post.new(x) }
5
+ end
6
+ end
7
+ end
@@ -1,13 +1,15 @@
1
- module RssParser
2
- def parse(rss_feed_url)
3
- raise Exception.new('RSS feed missing') unless rss_feed_url
4
- rss = RSS::Parser.parse(get_content(rss_feed_url), false)
5
- rss.items
6
- end
1
+ module Twog
2
+ module RssParser
3
+ def parse_feed(rss_feed_url)
4
+ raise Exception.new('RSS feed missing') unless rss_feed_url
5
+ rss = RSS::Parser.parse(get_content(rss_feed_url), false)
6
+ rss.items
7
+ end
7
8
 
8
- def get_content(rss_feed_url)
9
- open(rss_feed_url) do |f|
10
- f.read
9
+ def get_content(rss_feed_url)
10
+ open(rss_feed_url) do |f|
11
+ f.read
12
+ end
11
13
  end
12
14
  end
13
15
  end
@@ -1,51 +1,39 @@
1
- class RSS::Atom::Feed::Entry
2
- def <=>(other_entry)
3
- this_time = Time.parse(updated.content.to_s)
4
- other_time = Time.parse(other_entry.updated.content.to_s)
5
- if this_time < other_time
6
- -1
7
- elsif this_time > other_time
8
- 1
9
- else
10
- 0
1
+ module Twog
2
+ module TwitterHandler
3
+ def tweet(posts, conf, bitly)
4
+ return unless posts && posts.length > 0
5
+ raise Exception.new('OAuth Consumer Key missing') unless conf['consumer_key']
6
+ raise Exception.new('OAuth Consumer Secret missing') unless conf['consumer_secret']
7
+ raise Exception.new('OAuth Access Token missing') unless conf['access_token']
8
+ raise Exception.new('OAuth Access Secret missing') unless conf['access_secret']
9
+ posts.sort.each do |post|
10
+ link = bitly ? bitly.shorten(post.link).short_url : item.link
11
+ use_twitter_oauth(post, link, conf)
12
+ update_config_file_with_latest_tweet_date(post.date.to_s, conf)
13
+ end
11
14
  end
12
- end
13
- end
14
15
 
15
- module TwitterHandler
16
- def tweet(posts, conf, bitly)
17
- return unless posts && posts.length > 0
18
- raise Exception.new('OAuth Consumer Key missing') unless conf['consumer_key']
19
- raise Exception.new('OAuth Consumer Secret missing') unless conf['consumer_secret']
20
- raise Exception.new('OAuth Access Token missing') unless conf['access_token']
21
- raise Exception.new('OAuth Access Secret missing') unless conf['access_secret']
22
- posts.sort.each do |item|
23
- link = bitly ? bitly.shorten(item.link.href).short_url : item.link.href
24
- use_twitter_oauth(item, link, conf)
25
- update_config_file_with_latest_tweet_date(item.updated.content.to_s, conf)
16
+ def use_twitter_oauth(post, link, conf)
17
+ client = TwitterOAuth::Client.new(
18
+ :consumer_key => conf['consumer_key'],
19
+ :consumer_secret => conf['consumer_secret'],
20
+ :token => conf['access_token'],
21
+ :secret => conf['access_secret']
22
+ )
23
+ raise Exception.new('TwitterOAuth unauthorized') unless client.authorized?
24
+ text = ensure_text_is_of_length(140, post.title, link)
25
+ client.update(text)
26
26
  end
27
- end
28
-
29
- def use_twitter_oauth(item, link, conf)
30
- client = TwitterOAuth::Client.new(
31
- :consumer_key => conf['consumer_key'],
32
- :consumer_secret => conf['consumer_secret'],
33
- :token => conf['access_token'],
34
- :secret => conf['access_secret']
35
- )
36
- raise Exception.new('TwitterOAuth unauthorized') unless client.authorized?
37
- text = ensure_text_is_of_length(140, item.title.content, link)
38
- client.update(text)
39
- end
40
27
 
41
- def ensure_text_is_of_length(length, title, link)
42
- blogged = "blogged:"
43
- title = title[0,(length-((" "*2).length+blogged.length+link.length))]
44
- [blogged, title, link].join(' ')
45
- end
28
+ def ensure_text_is_of_length(length, title, link)
29
+ blogged = "blogged:"
30
+ title = title[0,(length-((" "*2).length+blogged.length+link.length))]
31
+ [blogged, title, link].join(' ')
32
+ end
46
33
 
47
- def update_config_file_with_latest_tweet_date(last_blog_post_tweeted, conf)
48
- conf['last_blog_post_tweeted'] = last_blog_post_tweeted
49
- File.open("#{ENV['HOME']}/.twog/conf.yaml","w") { |out| out.write(conf.to_yaml) }
34
+ def update_config_file_with_latest_tweet_date(last_blog_post_tweeted, conf)
35
+ conf['last_blog_post_tweeted'] = last_blog_post_tweeted
36
+ File.open("#{ENV['HOME']}/.twog/conf.yaml","w") { |out| out.write(conf.to_yaml) }
37
+ end
50
38
  end
51
39
  end
@@ -1,62 +1,66 @@
1
- require "lib/twog"
1
+ $: << File.join(File.dirname(__FILE__), "/../lib")
2
+
3
+ require 'rubygems'
4
+ require 'twog'
2
5
  require 'rss'
6
+ require 'simplecov'
7
+
8
+ include Twog
9
+
10
+ if ENV['COVERAGE']
11
+ require 'simplecov'
12
+ SimpleCov.start
13
+ end
14
+
15
+ def test_conf
16
+ @conf = {'rss_feed' => 'rss feed',
17
+ 'bitly_username' => 'username',
18
+ 'bitly_api_key' => 'api key',
19
+ 'consumer_key' => 'consumerkey',
20
+ 'consumer_secret' => 'consumersecret',
21
+ 'access_token' => 'accesstoken',
22
+ 'access_secret' => 'accesssecret',
23
+ 'last_blog_post_tweeted' => '31 Mar 2010 07:52:17 -0600'}
24
+ end
3
25
 
4
- module TwogSpecHelper
5
- def test_conf
6
- @conf = {'rss_feed' => 'rss feed',
7
- 'bitly_username' => 'username',
8
- 'bitly_api_key' => 'api key',
9
- 'consumer_key' => 'consumerkey',
10
- 'consumer_secret' => 'consumersecret',
11
- 'access_token' => 'accesstoken',
12
- 'access_secret' => 'accesssecret',
13
- 'last_blog_post_tweeted' => '31 Mar 2010 07:52:17 -0600'}
14
- end
15
-
16
- def rss_feed_entry
17
- entry=<<-EOS
18
- <entry>
19
- <title>Pair Programming</title>
20
- <link href="http://blog.jasonmeridth.com/2009/01/29/pair-programming.html"/>
21
- <updated>2009-01-29T00:00:00-08:00</updated>
22
- <id>http://blog.jasonmeridth.com/2009/01/29/pair-programming</id>
23
- <content type="html">&lt;h1&gt;Pair Programming&lt;/h1&gt;
24
- &lt;p class=&quot;meta&quot;&gt;29 Jan 2009 &amp;#8211; San Antonio&lt;/p&gt;
25
- &lt;p&gt;I recently found this post, &lt;a href=&quot;http://blog.jayfields.com/2008/02/pair-programming-all-time.html&quot;&gt;Pair Programming all the time&lt;/a&gt;, by &lt;a href=&quot;http://blog.jayfields.com/&quot;&gt;Jay Fields&lt;/a&gt; and loved it. I&amp;#8217;ve felt the same way about pair programming.&lt;/p&gt;
26
- &lt;blockquote&gt;
27
- &lt;p&gt;&amp;#8220;I define all the time (in terms of pairing) as when I&amp;#8217;m writing code that I&amp;#8217;m going to commit.&amp;#8221;&lt;/p&gt;
28
- &lt;/blockquote&gt;
29
- &lt;p&gt;That is perfect. Common sense but stated explicitly. I worked in an Agile shop for 2 1/2 years and the environment was setup to highlight pair programming. Pictures and little explanation &lt;a href=&quot;http://www.lostechies.com/blogs/joe_ocampo/archive/2007/12/09/where-the-magic-happens-our-dev-lap.aspx&quot;&gt;here&lt;/a&gt; (Thanks Joe). We even marked tasks in the stories as low (L) or high (H) to dictate whether a pair was necessary (this was decided during our modeling week by the two developers who tasked the story, but always up for discussion during the iteration). It worked out pretty well.&lt;/p&gt;
30
- &lt;p&gt;I understand and have heard all the reasons to not pair program. Sometimes it works and sometimes it doesn&amp;#8217;t. I&amp;#8217;ve personally experienced the benefits. You learn to work with different personalities and that can only benefit you in your professional career. And, the obvious reason, is immediate code review. But, as my friend Scott C. Reynolds &lt;a href=&quot;http://www.lostechies.com/blogs/scottcreynolds/archive/2009/01/23/on-teaching-learning-and-being-honest-with-ourselves.aspx&quot;&gt;says&lt;/a&gt; (more or less):&lt;/p&gt;
31
- &lt;blockquote&gt;
32
- &lt;p&gt;&amp;#8220;Not everyone is cut from the same cloth&amp;#8221;&lt;/p&gt;
33
- &lt;/blockquote&gt;
34
- &lt;p&gt;That is true and that is life. I hope this helps someone understand that not all pair programming enthusiasts are zealots. I know it&amp;#8217;s a fine line though.&lt;/p&gt;</content>
35
- </entry>
36
- EOS
37
- return entry
38
- end
39
-
40
- def rss_entry
41
- rss = RSS::Parser.parse(rss_feed_url_content)
42
- rss.items[0]
43
- end
44
-
45
- def rss_feed_url_content
46
- post=<<-EOS
47
- <?xml version="1.0" encoding="utf-8"?>
48
- <feed xmlns="http://www.w3.org/2005/Atom">
49
-
50
- <title>Jason Meridth</title>
51
- <link href="http://blog.jasonmeridth.com/atom.xml" rel="self"/>
52
- <link href="http://blog.jasonmeridth.com/"/>
53
- <updated>2010-04-04T13:15:31-07:00</updated>
54
- <id>http://blog.jasonmeridth.com/</id>
55
- <author>
56
- <name>Jason Meridth</name>
57
- <email>jmeridth@gmail.com</email>
58
- </author>
59
- EOS
60
- return "#{post}#{rss_feed_entry}"
61
- end
26
+ def rss_feed_entry
27
+ entry=<<-EOS
28
+ <item>
29
+ <title>Gocode Vim Plugin and Go Modules</title>
30
+ <description>
31
+ <p><strong>Update</strong>: My friend <a href="https://twitter.com/_seemethere">Eli Uriegas</a> let me know that you don’t need <code class="highlighter-rouge">gocode</code> anymore since <code class="highlighter-rouge">vim-go</code> has autocompletion. I tested it out and he is correct. <a href="https://twitter.com/_seemethere/status/1081626050717728770">Here</a> is his tweet with a link to his dotfiles on how he sets up his <code class="highlighter-rouge">.vimrc</code> to use <code class="highlighter-rouge">vim-go</code></p> <p><em>Original Post:</em></p> <p>I recently purchased <a href="https://lets-go.alexedwards.net/">Let’s Go</a> from Alex Edwards. I wanted an end-to-end Golang website tutorial. It has been great. Lots learned.</p> <p>Unfortunately, he is using Go’s modules and the version of the gocode Vim plugin I was using did not support Go modules.</p> <h3 id="solution">Solution:</h3> <p>Use <a href="https://github.com/stamblerre/gocode">this fork</a> of the gocode Vim plugin and you’ll get support for Go modules.</p> <p>I use <a href="https://github.com/junegunn/vim-plug">Vim Plug</a> for my Vim plugins. Huge fan of Vundle but I like the post-actions feature of Plug. I just had to change one line of my vimrc and re-run updates.</p> <div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gh">diff --git a/vimrc b/vimrc index 3e8edf1..8395705 100644 </span><span class="gd">--- a/vimrc </span><span class="gi">+++ b/vimrc </span><span class="gu">@@ -73,7 +73,7 @@ endif </span> let editor_name='nvim' Plug 'zchee/deoplete-go', { 'do': 'make'} endif <span class="gd">- Plug 'nsf/gocode', { 'rtp': 'vim', 'do': '~/.config/nvim/plugged/gocode/vim/symlink.sh' } </span><span class="gi">+ Plug 'stamblerre/gocode', { 'rtp': 'vim', 'do': '~/.vim/plugged/gocode/vim/symlink.sh' } </span> Plug 'godoctor/godoctor.vim', {'for': 'go'} " Gocode refactoring tool " } </code></pre></div></div> <p>That is the line I had to change then run <code class="highlighter-rouge">:PlugUpdate!</code> and the new plugin was installed.</p> <p>I figured all of this out due to <a href="https://github.com/zchee/deoplete-go/issues/134#issuecomment-435436305">this comment</a> by <a href="https://github.com/cippaciong">Tommaso Sardelli</a> on Github. Thank you Tommaso.</p>
32
+ </description>
33
+ <pubDate>Sat, 05 Jan 2019 11:09:26 -0600</pubDate>
34
+ <link>
35
+ https://blog.jasonmeridth.com/posts/gocode-vim-plugin-and-go-modules/
36
+ </link>
37
+ <guid isPermaLink="true">
38
+ https://blog.jasonmeridth.com/posts/gocode-vim-plugin-and-go-modules/
39
+ </guid>
40
+ <category>go</category>
41
+ <category>vim</category>
42
+ </item>
43
+ EOS
44
+ return entry
62
45
  end
46
+
47
+ def rss_entry
48
+ rss = RSS::Parser.parse(rss_feed_url_content)
49
+ rss.items[0]
50
+ end
51
+
52
+ def rss_feed_url_content
53
+ rss_header=<<-EOS
54
+ <rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
55
+ <channel>
56
+ <title>Learn, Converse, Share</title>
57
+ <description>My Personal Blog</description>
58
+ <link>https://blog.jasonmeridth.com/</link>
59
+ <atom:link href="https://blog.jasonmeridth.com/feed.xml" rel="self" type="application/rss+xml"/>
60
+ <pubDate>Fri, 01 Mar 2019 13:02:56 -0600</pubDate>
61
+ <lastBuildDate>Fri, 01 Mar 2019 13:02:56 -0600</lastBuildDate>
62
+ <generator>Jekyll v3.7.4</generator>
63
+ EOS
64
+ return "#{rss_header}#{rss_feed_entry}</channel></rss>"
65
+ end
66
+
@@ -0,0 +1,38 @@
1
+ require File.join(File.dirname(__FILE__), "/../spec_helper")
2
+
3
+ describe BlogPostsHandler do
4
+ include BlogPostsHandler
5
+
6
+ before(:each) do
7
+ post = Twog::Post.new(double('', pubDate: Time.now, link: 'http://tinyurl.com'))
8
+ @posts = [post]
9
+ @last_blog_post_tweeted = test_conf['last_blog_post_tweeted']
10
+ end
11
+
12
+ it "should determine that there is one new blog post to tweet" do
13
+ posts = get_new_blog_posts(@posts, @last_blog_post_tweeted)
14
+ expect(posts.length).to eq(1)
15
+ end
16
+
17
+ it "should return zero if posts are nil" do
18
+ posts = get_new_blog_posts(nil, @last_blog_post_tweeted)
19
+ expect(posts.length).to eq(0)
20
+ end
21
+
22
+ it "should return zero if no posts are passed" do
23
+ posts = get_new_blog_posts([], @last_blog_post_tweeted)
24
+ expect(posts.length).to eq(0)
25
+ end
26
+
27
+ it "should return the posts if there is no last_blog_post_tweeted in the conf file" do
28
+ posts = get_new_blog_posts(@posts, nil)
29
+ expect(posts.length).to eq(1)
30
+ end
31
+
32
+ it "should return zero posts if the date is older than the last blog post date tweeted" do
33
+ post = Twog::Post.new(double('', pubDate: (Date.parse(@last_blog_post_tweeted.to_s) - 10).to_s, link: 'http://tinyurl.com'))
34
+ @posts = [post]
35
+ posts = get_new_blog_posts(@posts, @last_blog_post_tweeted)
36
+ expect(posts.length).to eq(0)
37
+ end
38
+ end
@@ -0,0 +1,77 @@
1
+ require File.join(File.dirname(__FILE__), "/../spec_helper")
2
+
3
+ describe Post do
4
+ context "when rss entry has updated and link.href" do
5
+ before(:each) do
6
+ updated =double('update', content: '2010-04-02T01:00:00-06:00')
7
+ link = double('link', href: 'http://tinyurl.com')
8
+ title = double('title', content: 'test title')
9
+ @post = double('post', updated: updated, link: link, title: title)
10
+
11
+ end
12
+
13
+ it "should return a link" do
14
+ twog_post = Twog::Post.new(@post)
15
+ expect(twog_post.date).to eq("2010-04-02T01:00:00-06:00")
16
+ end
17
+
18
+ it "should return a date" do
19
+ twog_post = Twog::Post.new(@post)
20
+ expect(twog_post.date).to eq("2010-04-02T01:00:00-06:00")
21
+ end
22
+
23
+ it "should return a title" do
24
+ twog_post = Twog::Post.new(@post)
25
+ expect(twog_post.title).to eq("test title")
26
+ end
27
+
28
+ it "should sort multiple posts" do
29
+ unsorted = (1..10).sort_by { rand }.collect do |i|
30
+ updated = double('update', content: (Time.now + (60*60*24*i)).to_s)
31
+ link = double('link', href: 'http://tinyurl.com')
32
+ post = double('post', updated: updated, link: link)
33
+ Twog::Post.new(post)
34
+ end
35
+ sorted = unsorted.sort!
36
+ expect(sorted.length).to eq(10)
37
+
38
+ sorted.inject {|i, j| expect(Time.parse(i.date.to_s)).to be < Time.parse(j.date.to_s); j }
39
+ end
40
+
41
+ it "should leave meeting list alone if all the times are the same" do
42
+ unsorted = (1..10).collect do |i|
43
+ updated = double('update', content: (Time.now + (60*60*24*3)).to_s)
44
+ link = double('link', href: 'http://tinyurl.com')
45
+ post = double('post', updated: updated, link: link)
46
+ Twog::Post.new(post)
47
+ end
48
+ sorted = unsorted.sort!
49
+ expect(sorted.length).to eq(10)
50
+ expect(sorted).to eq(unsorted)
51
+ end
52
+ end
53
+
54
+ context "when rss entry has pubDate and link" do
55
+ before(:each) do
56
+ @post = double('post', pubDate: '2010-04-02T01:00:00-06:00', link: 'http://tinyurl.com')
57
+ end
58
+
59
+ it "should return a link" do
60
+ twog_post = Twog::Post.new(@post)
61
+
62
+ expect(twog_post.link).to eq("http://tinyurl.com")
63
+ expect(twog_post.date).to eq("2010-04-02T01:00:00-06:00")
64
+ end
65
+
66
+ it "should sort multiple posts again" do
67
+ unsorted = (1..10).sort_by { rand }.collect do |i|
68
+ post = double('post', pubDate: (Time.now + (60*60*24*i)).to_s, link: 'http://tinyurl.com')
69
+ Twog::Post.new(post)
70
+ end
71
+ sorted = unsorted.sort!
72
+ expect(sorted.length).to eq(10)
73
+
74
+ sorted.inject {|i, j| expect(Time.parse(i.date.to_s)).to be < Time.parse(j.date.to_s); j }
75
+ end
76
+ end
77
+ end