hackershout 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.
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Guardfile +8 -0
- data/Rakefile +11 -0
- data/Readme.md +92 -0
- data/bin/hackershout +5 -0
- data/hackershout.gemspec +30 -0
- data/lib/hackershout/base.rb +83 -0
- data/lib/hackershout/output.rb +13 -0
- data/lib/hackershout/provider/base.rb +40 -0
- data/lib/hackershout/provider/hackernews.rb +29 -0
- data/lib/hackershout/provider/reddit.rb +29 -0
- data/lib/hackershout/provider/rubyflow.rb +36 -0
- data/lib/hackershout/provider.rb +67 -0
- data/lib/hackershout/version.rb +3 -0
- data/lib/hackershout.rb +15 -0
- data/spec/hackershout/base_spec.rb +137 -0
- data/spec/hackershout/provider/base_spec.rb +52 -0
- data/spec/hackershout/provider_spec.rb +80 -0
- data/spec/hackershout_spec.rb +14 -0
- data/spec/spec_helper.rb +2 -0
- metadata +185 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create use ruby-1.9.2@hackershout
|
data/Gemfile
ADDED
data/Guardfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
7
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
task :default => :spec
|
11
|
+
task :test => [:spec]
|
data/Readme.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
#Hackershout
|
2
|
+
|
3
|
+
Shout your hackerness to the world!
|
4
|
+
|
5
|
+
Hackershout lets you promote your open-source work on the major Ruby (and geek
|
6
|
+
related) community blogs (Reddit, Hackernews and RubyFlow) via a nice command
|
7
|
+
line interface.
|
8
|
+
|
9
|
+
##Install
|
10
|
+
|
11
|
+
$ gem install hackershout
|
12
|
+
|
13
|
+
##Known caveats
|
14
|
+
|
15
|
+
Publishing to Reddit is not available yet.
|
16
|
+
|
17
|
+
##Notes
|
18
|
+
|
19
|
+
Through hackershout you publish an **URL** and a **text**. To link the URL from
|
20
|
+
the text you just have to use the special `<link>` tag. Note that for certain
|
21
|
+
sites, such as RubyFlow, you _have to link_ the URL from the text, since there
|
22
|
+
is no separate URL field. Hackershout will omit the special tag where it
|
23
|
+
doesn't need it (for example, in Reddit).
|
24
|
+
|
25
|
+
You can add additional links with normal HTML `<a>` tags.
|
26
|
+
|
27
|
+
Given `http://rubygems.org/gems/my_gem` as an URL, an example of a message
|
28
|
+
would be:
|
29
|
+
|
30
|
+
Hey! I have released <link>some awesome open-source gem</link> that is
|
31
|
+
going to save the world. Check it out! You can also grab the source code on
|
32
|
+
<a href="http://github.com/me/my_gem">Github</a> if you want.
|
33
|
+
|
34
|
+
This will publish in Reddit like this:
|
35
|
+
|
36
|
+
URL: http://rubygems.org/gems/my_gem
|
37
|
+
TEXT: Hey! I have released some awesome open-source gem that is going to
|
38
|
+
save the world. Check it out! You can also grab the source code on
|
39
|
+
<a href="http://github.com/me/my_gem">Github</a> if you want.
|
40
|
+
|
41
|
+
And on RubyFlow (where there is no separate URL field) like this:
|
42
|
+
|
43
|
+
TEXT: Hey! I have released <a href="http://rubygems.org/gems/my_gem">some
|
44
|
+
awesome open-source gem</a> that is going to save the world. Check it out!
|
45
|
+
You can also grab the source code on <a href="http://github.com/me/my_gem">
|
46
|
+
Github</a> if you want.
|
47
|
+
|
48
|
+
##Usage
|
49
|
+
|
50
|
+
$ hackershout
|
51
|
+
|
52
|
+
:: Welcome to hackershout! ::
|
53
|
+
|
54
|
+
Type the URL you want to share: http://rubygems.com/gems/my_gem
|
55
|
+
Enter a brief, descriptive title: Released MyGem 1.0!
|
56
|
+
|
57
|
+
Bear in mind that some services may require a more extended text aside from the title.
|
58
|
+
Type your message (two ENTERs to finish):
|
59
|
+
Hey! I have released <link>some awesome open-source gem</link> that is
|
60
|
+
going to save the world. Check it out! You can also grab the source code
|
61
|
+
on <a href="http://github.com/me/my_gem">Github</a> if you want.(enter)
|
62
|
+
(enter)
|
63
|
+
Type some tags separated by comma (i.e. ruby, rails, bdd): ruby, parsing
|
64
|
+
|
65
|
+
...Got it! Now where would you want to spread the word?
|
66
|
+
Ruby Reddit (y/n)? y
|
67
|
+
Sorry, I don't have your Ruby Reddit credentials.
|
68
|
+
E-mail: my.email@gmail.com
|
69
|
+
Password: ********
|
70
|
+
Saved! You won't have to enter your credentials for Ruby Reddit again.
|
71
|
+
Hackernews (y/n)? y
|
72
|
+
RubyFlow (y/n)? n
|
73
|
+
Fine.
|
74
|
+
Posting to Ruby Reddit...........ok
|
75
|
+
Posting to Hackernews............ok
|
76
|
+
|
77
|
+
Done. Happy hacking! :)
|
78
|
+
|
79
|
+
##Contribute!
|
80
|
+
|
81
|
+
* Fork the project.
|
82
|
+
* Make your feature addition or bug fix.
|
83
|
+
* Add specs for it. This is important so I don't break it in a future
|
84
|
+
version unintentionally.
|
85
|
+
* Commit, do not mess with rakefile, version, or history.
|
86
|
+
If you want to have your own version, that is fine but bump version
|
87
|
+
in a commit by itself I can ignore when I pull.
|
88
|
+
* Send me a pull request. Bonus points for topic branches.
|
89
|
+
|
90
|
+
## Copyright
|
91
|
+
|
92
|
+
Copyright (c) 2011 Codegram. See LICENSE for details.
|
data/bin/hackershout
ADDED
data/hackershout.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "hackershout/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "hackershout"
|
7
|
+
s.version = Hackershout::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Josep M. Bach", "Josep Jaume Rey", "Oriol Gual"]
|
10
|
+
s.email = ["info@codegram.com"]
|
11
|
+
s.homepage = "http://github.com/codegram/hackershout"
|
12
|
+
s.summary = %q{Shout your hackerness! Promote your work on Reddit, Hackernews and Ruby Flow.}
|
13
|
+
s.description = %q{Shout your hackerness! Promote your work on Reddit, Hackernews and Ruby Flow.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "hackershout"
|
16
|
+
|
17
|
+
s.add_runtime_dependency 'nokogiri'
|
18
|
+
s.add_runtime_dependency 'mechanize'
|
19
|
+
|
20
|
+
s.add_development_dependency 'rspec', '~> 2.5.0'
|
21
|
+
s.add_development_dependency 'guard'
|
22
|
+
s.add_development_dependency 'guard-rspec'
|
23
|
+
s.add_development_dependency 'rb-fsevent'
|
24
|
+
s.add_development_dependency 'vcr'
|
25
|
+
|
26
|
+
s.files = `git ls-files`.split("\n")
|
27
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
28
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'hackershout/output'
|
2
|
+
|
3
|
+
module Hackershout
|
4
|
+
class Base
|
5
|
+
include Output
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@url = options[:url]
|
9
|
+
@title = options[:title]
|
10
|
+
@message = options[:message]
|
11
|
+
@tags = options[:tags] || []
|
12
|
+
@providers = options[:providers] || []
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
welcome_banner
|
17
|
+
@url = ask_for_url
|
18
|
+
@title = ask_for_title
|
19
|
+
@message = ask_for_message
|
20
|
+
@tags = ask_for_tags
|
21
|
+
providers_banner
|
22
|
+
|
23
|
+
@providers = ask_for_providers
|
24
|
+
post_to_providers
|
25
|
+
end
|
26
|
+
|
27
|
+
def welcome_banner
|
28
|
+
print ":: Welcome to hackershout! ::"
|
29
|
+
blank
|
30
|
+
end
|
31
|
+
|
32
|
+
def ask_for_url
|
33
|
+
print "Type the URL you want to share: "
|
34
|
+
gets.chomp.strip
|
35
|
+
end
|
36
|
+
|
37
|
+
def ask_for_title
|
38
|
+
print "Enter a brief, descriptive title: "
|
39
|
+
gets.chomp.strip
|
40
|
+
end
|
41
|
+
|
42
|
+
def ask_for_message
|
43
|
+
print "Bear in mind that some services may require a more extended text aside from the title."
|
44
|
+
print "Type your message (two ENTERs to finish): "
|
45
|
+
$/ = "\n\n"
|
46
|
+
gets.chomp.strip.tap do
|
47
|
+
# Restore EOM char
|
48
|
+
$/ = "\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def ask_for_tags
|
53
|
+
print "Type some tags separated by comma (i.e. ruby, rails, bdd): "
|
54
|
+
gets.chomp.strip.split(',').map(&:strip)
|
55
|
+
end
|
56
|
+
|
57
|
+
def providers_banner
|
58
|
+
print "...Got it! Now where would you want to spread the word?"
|
59
|
+
end
|
60
|
+
|
61
|
+
def ask_for_providers
|
62
|
+
Provider.list.keys.select do |provider|
|
63
|
+
Provider.wants?(provider)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def post_to_providers
|
68
|
+
print "Fine."
|
69
|
+
@providers.map do |provider|
|
70
|
+
eval("Provider::#{provider.capitalize}").new({
|
71
|
+
url: @url,
|
72
|
+
title: @title,
|
73
|
+
message: @message,
|
74
|
+
tags: @tags,
|
75
|
+
})
|
76
|
+
end.each do |provider|
|
77
|
+
provider.publish
|
78
|
+
end
|
79
|
+
puts "Done. Happy hacking! :)\n"
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'mechanize'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
Mechanize.html_parser = Nokogiri::HTML
|
5
|
+
|
6
|
+
module Hackershout
|
7
|
+
module Provider
|
8
|
+
class Base
|
9
|
+
attr_reader :agent
|
10
|
+
|
11
|
+
include Output
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
@name = self.class.name.split("::").last
|
15
|
+
@url = options[:url]
|
16
|
+
@title = options[:title]
|
17
|
+
@tags = options[:tags]
|
18
|
+
@message = options[:message]
|
19
|
+
|
20
|
+
creds = YAML.load(File.read(File.join(File.expand_path('~'), '.hackershoutrc')))[@name.downcase]
|
21
|
+
raise "Can't find credentials on ~/.hackershoutrc for #{@name}" unless creds
|
22
|
+
|
23
|
+
@login = creds["login"]
|
24
|
+
@password = creds["password"]
|
25
|
+
@agent = Mechanize.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def publish
|
29
|
+
print "Posting to #{@name}..."
|
30
|
+
post unless ENV['NOSEND']
|
31
|
+
print "Posted to #{@name}!"
|
32
|
+
end
|
33
|
+
|
34
|
+
def post
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Hackershout
|
2
|
+
module Provider
|
3
|
+
class Hackernews < Base
|
4
|
+
|
5
|
+
URL = {
|
6
|
+
:login => 'http://news.ycombinator.com/submit'
|
7
|
+
}
|
8
|
+
|
9
|
+
def post
|
10
|
+
page = agent.get(URL[:login])
|
11
|
+
login_form = page.forms.first
|
12
|
+
login_form.u = @login
|
13
|
+
login_form.p = @password
|
14
|
+
page = login_form.submit
|
15
|
+
submit(page)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def submit(submit_page)
|
21
|
+
form = submit_page.forms.first
|
22
|
+
form.t = @title
|
23
|
+
form.u = @url
|
24
|
+
form.submit
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Hackershout
|
2
|
+
module Provider
|
3
|
+
class Reddit < Base
|
4
|
+
|
5
|
+
URL = {
|
6
|
+
:login => 'http://www.reddit.com/r/ruby/',
|
7
|
+
:submit => 'http://www.reddit.com/r/ruby/submit',
|
8
|
+
}
|
9
|
+
|
10
|
+
# Not logging in! :(
|
11
|
+
def post
|
12
|
+
raise NotImplementedError
|
13
|
+
page = agent.get(URL[:login])
|
14
|
+
login_form = page.forms_with(:action => 'http://www.reddit.com/r/ruby/post/login').first
|
15
|
+
login_form.user = @login
|
16
|
+
login_form.passwd = @password
|
17
|
+
page = login_form.submit
|
18
|
+
puts page.links_with(:text => 'logout').inspect
|
19
|
+
page = page.links_with(:text => 'Submit a link').first.click
|
20
|
+
puts page.inspect
|
21
|
+
|
22
|
+
rescue Mechanize::ResponseCodeError=>e
|
23
|
+
puts "#{@name} is unavailable for now. Please try in a few minutes. The server gave the following reason:"
|
24
|
+
puts e.inspect
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Hackershout
|
2
|
+
module Provider
|
3
|
+
class Rubyflow < Base
|
4
|
+
|
5
|
+
URL = {
|
6
|
+
:login => 'http://www.rubyflow.com/login'
|
7
|
+
}
|
8
|
+
|
9
|
+
def post
|
10
|
+
page = agent.get(URL[:login])
|
11
|
+
login_form = page.form_with(:action => '/session')
|
12
|
+
login_form.login = @login
|
13
|
+
login_form.password = @password
|
14
|
+
page = login_form.submit
|
15
|
+
page = page.link_with(:href => /items\/new/).click
|
16
|
+
submit(page)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def submit(submit_page)
|
22
|
+
form = submit_page.form_with(:action => '/items')
|
23
|
+
form['item[title]'] = @title
|
24
|
+
form['item[content]'] = process(@message)
|
25
|
+
form.submit
|
26
|
+
end
|
27
|
+
|
28
|
+
def process(message)
|
29
|
+
message.gsub!('<link>', "<a href=\"#{@url}\">")
|
30
|
+
message.gsub!('</link>', "</a>")
|
31
|
+
message
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Hackershout
|
2
|
+
module Provider
|
3
|
+
class << self
|
4
|
+
|
5
|
+
include Output
|
6
|
+
|
7
|
+
def list
|
8
|
+
{
|
9
|
+
# :reddit => 'Ruby Reddit',
|
10
|
+
:hackernews => 'Hackernews',
|
11
|
+
:rubyflow => 'RubyFlow',
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def wants?(provider)
|
16
|
+
name = list[provider]
|
17
|
+
print "\t#{name} (y/n)? "
|
18
|
+
return case gets.chomp
|
19
|
+
when 'y'
|
20
|
+
check_credentials_for(provider)
|
21
|
+
when 'n'
|
22
|
+
false
|
23
|
+
else
|
24
|
+
blank
|
25
|
+
print "I don't understand. Just type y for yes and n for no :)"
|
26
|
+
wants?(provider)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def check_credentials_for(provider)
|
33
|
+
credentials = if File.exists?(File.join(File.expand_path('~'), '.hackershoutrc'))
|
34
|
+
YAML.load(File.read(File.join(File.expand_path('~'), '.hackershoutrc')))
|
35
|
+
else
|
36
|
+
ask_for_credentials(provider)
|
37
|
+
end
|
38
|
+
|
39
|
+
if credentials[provider.to_s] &&
|
40
|
+
credentials[provider.to_s]["login"] &&
|
41
|
+
credentials[provider.to_s]["password"]
|
42
|
+
true
|
43
|
+
else
|
44
|
+
ask_for_credentials(provider)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def ask_for_credentials(provider)
|
49
|
+
print "Sorry, I don't have your #{list[provider]} credentials."
|
50
|
+
print "\tLogin: "
|
51
|
+
login = gets.chomp.strip
|
52
|
+
system('stty -echo')
|
53
|
+
print "\tPassword: "
|
54
|
+
password = gets.chomp.strip
|
55
|
+
system('stty echo')
|
56
|
+
File.open(File.join(File.expand_path('~'), '.hackershoutrc'), 'a') do |file|
|
57
|
+
file.write "\n#{provider}:"
|
58
|
+
file.write "\n login: #{login}"
|
59
|
+
file.write "\n password: #{password}"
|
60
|
+
file.write "\n"
|
61
|
+
end
|
62
|
+
{ provider => { "login" => login, "password" => password } }
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/hackershout.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'hackershout/output'
|
2
|
+
require 'hackershout/provider'
|
3
|
+
require 'hackershout/provider/base'
|
4
|
+
require 'hackershout/provider/reddit'
|
5
|
+
require 'hackershout/provider/hackernews'
|
6
|
+
require 'hackershout/provider/rubyflow'
|
7
|
+
require 'hackershout/base'
|
8
|
+
|
9
|
+
module Hackershout
|
10
|
+
class << self
|
11
|
+
def run
|
12
|
+
Base.new.run
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Hackershout
|
4
|
+
describe Base do
|
5
|
+
|
6
|
+
describe ".run" do
|
7
|
+
before do
|
8
|
+
subject.stub(:welcome_banner)
|
9
|
+
subject.stub(:ask_for_url)
|
10
|
+
subject.stub(:ask_for_message)
|
11
|
+
subject.stub(:ask_for_title)
|
12
|
+
subject.stub(:ask_for_tags)
|
13
|
+
subject.stub(:providers_banner)
|
14
|
+
subject.stub(:ask_for_providers)
|
15
|
+
subject.stub(:post_to_providers)
|
16
|
+
end
|
17
|
+
it 'calls welcome banner' do
|
18
|
+
subject.should_receive(:welcome_banner)
|
19
|
+
subject.run
|
20
|
+
end
|
21
|
+
it 'calls ask_for_url' do
|
22
|
+
subject.should_receive(:ask_for_url)
|
23
|
+
subject.run
|
24
|
+
end
|
25
|
+
it 'calls ask_for_title' do
|
26
|
+
subject.should_receive(:ask_for_title)
|
27
|
+
subject.run
|
28
|
+
end
|
29
|
+
it 'calls ask_for_message' do
|
30
|
+
subject.should_receive(:ask_for_message)
|
31
|
+
subject.run
|
32
|
+
end
|
33
|
+
it 'calls ask_for_tags' do
|
34
|
+
subject.should_receive(:ask_for_tags)
|
35
|
+
subject.run
|
36
|
+
end
|
37
|
+
it 'calls providers_banner' do
|
38
|
+
subject.should_receive(:providers_banner)
|
39
|
+
subject.run
|
40
|
+
end
|
41
|
+
it 'calls ask_for_providers' do
|
42
|
+
subject.should_receive(:ask_for_providers)
|
43
|
+
subject.run
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#welcome_banner" do
|
48
|
+
it 'prints the welcome banner' do
|
49
|
+
subject.should_receive(:blank).any_number_of_times
|
50
|
+
subject.should_receive(:print).with(":: Welcome to hackershout! ::")
|
51
|
+
subject.welcome_banner
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#ask_for_url" do
|
56
|
+
it 'asks for the url and returns it' do
|
57
|
+
subject.should_receive(:print).with("Type the URL you want to share: ")
|
58
|
+
subject.should_receive(:gets).and_return ' http://rubygems.org/gems/my_gem '
|
59
|
+
subject.ask_for_url.should == 'http://rubygems.org/gems/my_gem'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#ask_for_title" do
|
64
|
+
it 'asks for the title and returns it' do
|
65
|
+
subject.should_receive(:print).with("Enter a brief, descriptive title: ")
|
66
|
+
subject.should_receive(:gets).and_return ' Released my gem: a nifty gem to eradicate hunger! '
|
67
|
+
subject.ask_for_title.should == 'Released my gem: a nifty gem to eradicate hunger!'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#ask_for_message" do
|
72
|
+
it 'asks for the message and returns it' do
|
73
|
+
subject.should_receive(:print).with("Bear in mind that some services may require a more extended text aside from the title.")
|
74
|
+
subject.should_receive(:print).with("Type your message (two ENTERs to finish): ")
|
75
|
+
subject.should_receive(:gets).and_return("""
|
76
|
+
Hey! I have released <link>some awesome open-source gem</link> that is
|
77
|
+
going to save the world.\nCheck it out! You can also grab the source code
|
78
|
+
on <a href=\"http://github.com/me/my_gem\">Github</a> if you want.
|
79
|
+
|
80
|
+
""")
|
81
|
+
subject.ask_for_message
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#ask_for_tags" do
|
86
|
+
it 'asks for the tags and returns them' do
|
87
|
+
subject.should_receive(:print).with("Type some tags separated by comma (i.e. ruby, rails, bdd): ")
|
88
|
+
subject.should_receive(:gets).and_return ' ruby , bdd, rails, parsing '
|
89
|
+
subject.ask_for_tags.should == %w( ruby bdd rails parsing )
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#providers_banner" do
|
94
|
+
it 'prints the providers banner' do
|
95
|
+
subject.should_receive(:print).with("...Got it! Now where would you want to spread the word?")
|
96
|
+
subject.providers_banner
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#ask_for_providers" do
|
101
|
+
it 'asks for providers' do
|
102
|
+
providers = [:reddit, :hackernews, :rubyflow]
|
103
|
+
Provider.stub_chain('list.keys').and_return providers
|
104
|
+
Provider.should_receive(:wants?).with(:reddit).and_return true
|
105
|
+
Provider.should_receive(:wants?).with(:hackernews).and_return false
|
106
|
+
Provider.should_receive(:wants?).with(:rubyflow).and_return true
|
107
|
+
|
108
|
+
subject.ask_for_providers.should == [:reddit, :rubyflow]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#post_to_providers" do
|
113
|
+
it 'instantiates each provider and calls #publish on it' do
|
114
|
+
args = {
|
115
|
+
:url => 'http://rubygems.org/gems/my_gem',
|
116
|
+
:title => 'Released MyGem!',
|
117
|
+
:tags => ['ruby', 'bdd'],
|
118
|
+
:message => 'Hello!',
|
119
|
+
}
|
120
|
+
base = Base.new({:providers => [:reddit, :hackernews]}.update(args))
|
121
|
+
base.stub(:print)
|
122
|
+
|
123
|
+
hackernews = double :provider
|
124
|
+
reddit = double :provider
|
125
|
+
|
126
|
+
Provider::Hackernews.should_receive(:new).with(args).and_return hackernews
|
127
|
+
Provider::Reddit.should_receive(:new).with(args).and_return reddit
|
128
|
+
|
129
|
+
hackernews.should_receive(:publish)
|
130
|
+
reddit.should_receive(:publish)
|
131
|
+
|
132
|
+
base.post_to_providers
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Hackershout
|
4
|
+
module Provider
|
5
|
+
describe Base do
|
6
|
+
|
7
|
+
# Test only base class. Each provider only implements #post with pure Mechanize logic.
|
8
|
+
|
9
|
+
before do
|
10
|
+
File.stub(:read).and_return """
|
11
|
+
base:
|
12
|
+
login: my.email@gmail.com
|
13
|
+
password: mypassword
|
14
|
+
"""
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#initialize" do
|
18
|
+
it 'reads the credentials and stores them' do
|
19
|
+
base = Base.new(:url => 'url',
|
20
|
+
:tags => ['my', 'tags'],
|
21
|
+
:message => 'message')
|
22
|
+
|
23
|
+
base.instance_variable_get(:@login).should eq('my.email@gmail.com')
|
24
|
+
base.instance_variable_get(:@password).should eq('mypassword')
|
25
|
+
base.instance_variable_get(:@agent).should be_a(Mechanize)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#publish" do
|
30
|
+
it 'logs in and posts' do
|
31
|
+
base = Base.new(:url => 'url',
|
32
|
+
:tags => ['my', 'tags'],
|
33
|
+
:message => 'message')
|
34
|
+
|
35
|
+
base.should_receive(:post)
|
36
|
+
|
37
|
+
base.publish
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'crap' do
|
42
|
+
File.stub(:read).and_return """
|
43
|
+
hackernews:
|
44
|
+
login: codegram
|
45
|
+
password: codgamerP90
|
46
|
+
"""
|
47
|
+
Hackernews.new(:title => 'my title', :message => 'my message', :url => 'http://google.com').post
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Hackershout
|
4
|
+
describe Provider do
|
5
|
+
|
6
|
+
describe "#list" do
|
7
|
+
it 'returns a hash' do
|
8
|
+
subject.list.should == {
|
9
|
+
:reddit => 'Ruby Reddit',
|
10
|
+
:hackernews => 'Hackernews',
|
11
|
+
:rubyflow => 'RubyFlow',
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#wants?(provider)" do
|
17
|
+
before do
|
18
|
+
subject.should_receive(:print).with("\tRuby Reddit (y/n)? ")
|
19
|
+
end
|
20
|
+
context 'if the user wants to publish on a provider' do
|
21
|
+
it 'checks the credentials' do
|
22
|
+
subject.should_receive(:gets).and_return 'y'
|
23
|
+
subject.should_receive(:check_credentials_for).with(:reddit)
|
24
|
+
subject.wants?(:reddit)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
context 'if the user doesn\'t want' do
|
28
|
+
it 'returns false' do
|
29
|
+
subject.should_receive(:gets).and_return 'n'
|
30
|
+
subject.wants?(:reddit).should be_false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#check_credentials_for(provider)" do
|
36
|
+
|
37
|
+
context 'if the credentials exist' do
|
38
|
+
it 'returns true' do
|
39
|
+
File.stub(:read).and_return "reddit:\n login: my.email@gmail.com\n password: mypassword"
|
40
|
+
subject.should_not_receive(:ask_for_credentials)
|
41
|
+
subject.send(:check_credentials_for, :reddit).should be_true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'otherwise' do
|
46
|
+
it 'asks for them' do
|
47
|
+
File.stub(:read).and_return "hackernews:\n login: my.email@gmail.com\n password: mypassword"
|
48
|
+
subject.should_receive(:ask_for_credentials).with(:reddit)
|
49
|
+
subject.send(:check_credentials_for, :reddit)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "if the file doesn't even exist" do
|
54
|
+
it 'asks for the credentials as well' do
|
55
|
+
File.stub(:read).and_raise Errno::ENOENT
|
56
|
+
subject.should_receive(:ask_for_credentials).with(:reddit)
|
57
|
+
subject.send(:check_credentials_for, :reddit)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#ask_for_credentials(provider)" do
|
64
|
+
it 'asks for the credentials and saves them into ~/.hackershoutrc' do
|
65
|
+
subject.stub(:print)
|
66
|
+
file = double :file
|
67
|
+
subject.should_receive(:gets).and_return 'my.email@gmail.com'
|
68
|
+
subject.should_receive(:gets).and_return 'mypassword'
|
69
|
+
File.should_receive(:open).and_yield file
|
70
|
+
|
71
|
+
file.should_receive(:write).with "\nreddit:"
|
72
|
+
file.should_receive(:write).with "\n login: my.email@gmail.com"
|
73
|
+
file.should_receive(:write).with "\n password: mypassword"
|
74
|
+
|
75
|
+
subject.send(:ask_for_credentials, :reddit)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hackershout do
|
4
|
+
|
5
|
+
describe ".run" do
|
6
|
+
it 'calls run on a new Base instance' do
|
7
|
+
Hackershout::Base.should_receive(:new).and_return(double(:base).tap { |b|
|
8
|
+
b.should_receive(:run)
|
9
|
+
})
|
10
|
+
subject.run
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hackershout
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Josep M. Bach
|
13
|
+
- Josep Jaume Rey
|
14
|
+
- Oriol Gual
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-02-17 00:00:00 +01:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: nokogiri
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: mechanize
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rspec
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ~>
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
segments:
|
57
|
+
- 2
|
58
|
+
- 5
|
59
|
+
- 0
|
60
|
+
version: 2.5.0
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: guard
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
type: :development
|
75
|
+
version_requirements: *id004
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: guard-rspec
|
78
|
+
prerelease: false
|
79
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
segments:
|
85
|
+
- 0
|
86
|
+
version: "0"
|
87
|
+
type: :development
|
88
|
+
version_requirements: *id005
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rb-fsevent
|
91
|
+
prerelease: false
|
92
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
segments:
|
98
|
+
- 0
|
99
|
+
version: "0"
|
100
|
+
type: :development
|
101
|
+
version_requirements: *id006
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: vcr
|
104
|
+
prerelease: false
|
105
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
segments:
|
111
|
+
- 0
|
112
|
+
version: "0"
|
113
|
+
type: :development
|
114
|
+
version_requirements: *id007
|
115
|
+
description: Shout your hackerness! Promote your work on Reddit, Hackernews and Ruby Flow.
|
116
|
+
email:
|
117
|
+
- info@codegram.com
|
118
|
+
executables:
|
119
|
+
- hackershout
|
120
|
+
extensions: []
|
121
|
+
|
122
|
+
extra_rdoc_files: []
|
123
|
+
|
124
|
+
files:
|
125
|
+
- .gitignore
|
126
|
+
- .rspec
|
127
|
+
- .rvmrc
|
128
|
+
- Gemfile
|
129
|
+
- Guardfile
|
130
|
+
- Rakefile
|
131
|
+
- Readme.md
|
132
|
+
- bin/hackershout
|
133
|
+
- hackershout.gemspec
|
134
|
+
- lib/hackershout.rb
|
135
|
+
- lib/hackershout/base.rb
|
136
|
+
- lib/hackershout/output.rb
|
137
|
+
- lib/hackershout/provider.rb
|
138
|
+
- lib/hackershout/provider/base.rb
|
139
|
+
- lib/hackershout/provider/hackernews.rb
|
140
|
+
- lib/hackershout/provider/reddit.rb
|
141
|
+
- lib/hackershout/provider/rubyflow.rb
|
142
|
+
- lib/hackershout/version.rb
|
143
|
+
- spec/hackershout/base_spec.rb
|
144
|
+
- spec/hackershout/provider/base_spec.rb
|
145
|
+
- spec/hackershout/provider_spec.rb
|
146
|
+
- spec/hackershout_spec.rb
|
147
|
+
- spec/spec_helper.rb
|
148
|
+
has_rdoc: true
|
149
|
+
homepage: http://github.com/codegram/hackershout
|
150
|
+
licenses: []
|
151
|
+
|
152
|
+
post_install_message:
|
153
|
+
rdoc_options: []
|
154
|
+
|
155
|
+
require_paths:
|
156
|
+
- lib
|
157
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
158
|
+
none: false
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
segments:
|
163
|
+
- 0
|
164
|
+
version: "0"
|
165
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
166
|
+
none: false
|
167
|
+
requirements:
|
168
|
+
- - ">="
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
segments:
|
171
|
+
- 0
|
172
|
+
version: "0"
|
173
|
+
requirements: []
|
174
|
+
|
175
|
+
rubyforge_project: hackershout
|
176
|
+
rubygems_version: 1.3.7
|
177
|
+
signing_key:
|
178
|
+
specification_version: 3
|
179
|
+
summary: Shout your hackerness! Promote your work on Reddit, Hackernews and Ruby Flow.
|
180
|
+
test_files:
|
181
|
+
- spec/hackershout/base_spec.rb
|
182
|
+
- spec/hackershout/provider/base_spec.rb
|
183
|
+
- spec/hackershout/provider_spec.rb
|
184
|
+
- spec/hackershout_spec.rb
|
185
|
+
- spec/spec_helper.rb
|