├── .document ├── .gitignore ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.markdown ├── Rakefile ├── bin └── stars ├── lib ├── stars.rb └── stars │ ├── client.rb │ ├── config.rb │ ├── core_ext │ └── string.rb │ ├── post.rb │ └── services │ ├── convore.rb │ ├── favstar.rb │ └── service.rb ├── stars.gemspec └── test ├── examples └── stars.yml ├── helper.rb ├── test_client.rb ├── test_config.rb ├── test_favstar.rb └── test_post.rb /.document: -------------------------------------------------------------------------------- 1 | README.rdoc 2 | lib/**/*.rb 3 | bin/* 4 | features/**/*.feature 5 | LICENSE 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## MAC OS 2 | .DS_Store 3 | 4 | ## TEXTMATE 5 | *.tmproj 6 | tmtags 7 | 8 | ## EMACS 9 | *~ 10 | \#* 11 | .\#* 12 | 13 | ## VIM 14 | *.swp 15 | 16 | ## PROJECT::GENERAL 17 | coverage 18 | rdoc 19 | pkg 20 | 21 | ## PROJECT::SPECIFIC 22 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | gemspec 3 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | stars (0.5.0) 5 | httparty 6 | keep (~> 0.0.3) 7 | nokogiri 8 | terminal-table 9 | 10 | GEM 11 | remote: http://rubygems.org/ 12 | specs: 13 | crack (0.1.8) 14 | httparty (0.7.6) 15 | crack (= 0.1.8) 16 | keep (0.0.3) 17 | mocha (0.9.12) 18 | nokogiri (1.4.4) 19 | terminal-table (1.4.2) 20 | 21 | PLATFORMS 22 | ruby 23 | 24 | DEPENDENCIES 25 | mocha (~> 0.9.9) 26 | stars! 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Zach Holman 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # stars 2 | ### you're so vain / you probably think these stars are about you 3 | 4 | ## why 5 | 6 | Stars are a global currency. You tweet something, you write something, you 7 | otherwise put yourself out there. Sure, you feel a warm sense of accomplishment 8 | when you do this, but how do you *really* know if the people like you? 9 | 10 | **FUCKING STARS, THAT'S HOW** 11 | 12 | `stars` is a command-line client to check your precious stars at fine 13 | establishments like [Twitter](http://twitter.com) (via the exquisite 14 | [Favstar](http://favstar.fm)) and [Convore](http://convore.com). More services 15 | will be added as they get added. 16 | 17 | ## how 18 | 19 | Install with `gem install stars`, then do a quick `stars`, fill in your 20 | username for your service, and let it do its sultry magic. This is me lately: 21 | 22 | ★ stars 23 | +----+---------+-------+-----------------------------------------+ 24 | | | Service | Stars | The Hotness | 25 | +----+---------+-------+-----------------------------------------+ 26 | | 1 | Convore | 29 | Number of stars received | 27 | | 2 | Favstar | 5 | we're flawed because we want so much... | 28 | | 3 | Favstar | 3 | Cherish your drunken coworker moment... | 29 | | 4 | Favstar | 5 | Usually @kneath teaches me that a li... | 30 | | 5 | Favstar | 2 | Whew. Done with cohosting #codeconf.... | 31 | | 6 | Favstar | 2 | 1) Guys do lightning talk 2) Mention... | 32 | | 7 | Favstar | 1 | I am in a suit, and I'm addressing t... | 33 | | 8 | Favstar | 3 | @kneath you have to take a picture o... | 34 | | 9 | Favstar | 3 | I won't work until GitHub defunds fr... | 35 | | 10 | Favstar | 1 | Coordinating my attire for @codeconf... | 36 | | 11 | Favstar | 1 | You are the wind beneath my wings! I... | 37 | | 12 | Favstar | 1 | One of those rare San Francisco nigh... | 38 | | 13 | Favstar | 1 | @GavinStark Me too! | 39 | | 14 | Favstar | 30 | Why @github hacks on side projects: ... | 40 | | 15 | Favstar | 1 | @luckiestmonkey all the borg would g... | 41 | +----+---------+-------+-----------------------------------------+ 42 | 43 | ## BONUS ROUND 44 | 45 | Plopped a whopper of a comparison on Convore between Python's whitespacing and 46 | the piddly lawyer in Jurassic Park, and want to see the results *right now, 47 | dammit??!??!?!?!*? Have no fear! Check your service stars directly with `stars 48 | `: 49 | 50 | stars convore 51 | 52 | ## whip it out 53 | 54 | `stars` is kind of a silly little project. It's not as well-tested or 55 | well-documented as I'd like, but so it goes. Fork, send me a pull request, and 56 | we'll all be friends. 57 | 58 | ## who 59 | 60 | [@holman](http://twitter.com/holman) did this. I look great in a Speedo. 61 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | require 'date' 4 | 5 | ############################################################################# 6 | # 7 | # Helper functions 8 | # 9 | ############################################################################# 10 | 11 | def name 12 | @name ||= Dir['*.gemspec'].first.split('.').first 13 | end 14 | 15 | def version 16 | line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/] 17 | line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1] 18 | end 19 | 20 | def date 21 | Date.today.to_s 22 | end 23 | 24 | def rubyforge_project 25 | name 26 | end 27 | 28 | def gemspec_file 29 | "#{name}.gemspec" 30 | end 31 | 32 | def gem_file 33 | "#{name}-#{version}.gem" 34 | end 35 | 36 | def replace_header(head, header_name) 37 | head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"} 38 | end 39 | 40 | ############################################################################# 41 | # 42 | # Standard tasks 43 | # 44 | ############################################################################# 45 | 46 | task :default => :test 47 | 48 | require 'rake/testtask' 49 | Rake::TestTask.new(:test) do |test| 50 | test.libs << 'lib' << 'test' 51 | test.pattern = 'test/**/test_*.rb' 52 | test.verbose = true 53 | end 54 | 55 | desc "Generate RCov test coverage and open in your browser" 56 | task :coverage do 57 | require 'rcov' 58 | sh "rm -fr coverage" 59 | sh "rcov test/test_*.rb" 60 | sh "open coverage/index.html" 61 | end 62 | 63 | require 'rake/rdoctask' 64 | Rake::RDocTask.new do |rdoc| 65 | rdoc.rdoc_dir = 'rdoc' 66 | rdoc.title = "#{name} #{version}" 67 | rdoc.rdoc_files.include('README*') 68 | rdoc.rdoc_files.include('lib/**/*.rb') 69 | end 70 | 71 | desc "Open an irb session preloaded with this library" 72 | task :console do 73 | sh "irb -rubygems -r ./lib/#{name}.rb" 74 | end 75 | 76 | ############################################################################# 77 | # 78 | # Custom tasks (add your own tasks here) 79 | # 80 | ############################################################################# 81 | 82 | 83 | 84 | ############################################################################# 85 | # 86 | # Packaging tasks 87 | # 88 | ############################################################################# 89 | 90 | desc "Create tag v#{version} and build and push #{gem_file} to Rubygems" 91 | task :release => :build do 92 | unless `git branch` =~ /^\* master$/ 93 | puts "You must be on the master branch to release!" 94 | exit! 95 | end 96 | sh "git commit --allow-empty -a -m 'Release #{version}'" 97 | sh "git tag v#{version}" 98 | sh "git push origin master" 99 | sh "git push origin v#{version}" 100 | sh "gem push pkg/#{name}-#{version}.gem" 101 | end 102 | 103 | desc "Build #{gem_file} into the pkg directory" 104 | task :build => :gemspec do 105 | sh "mkdir -p pkg" 106 | sh "gem build #{gemspec_file}" 107 | sh "mv #{gem_file} pkg" 108 | end 109 | 110 | desc "Generate #{gemspec_file}" 111 | task :gemspec => :validate do 112 | # read spec file and split out manifest section 113 | spec = File.read(gemspec_file) 114 | head, manifest, tail = spec.split(" # = MANIFEST =\n") 115 | 116 | # replace name version and date 117 | replace_header(head, :name) 118 | replace_header(head, :version) 119 | replace_header(head, :date) 120 | #comment this out if your rubyforge_project has a different name 121 | replace_header(head, :rubyforge_project) 122 | 123 | # determine file list from git ls-files 124 | files = `git ls-files`. 125 | split("\n"). 126 | sort. 127 | reject { |file| file =~ /^\./ }. 128 | reject { |file| file =~ /^(rdoc|pkg)/ }. 129 | map { |file| " #{file}" }. 130 | join("\n") 131 | 132 | # piece file back together and write 133 | manifest = " s.files = %w[\n#{files}\n ]\n" 134 | spec = [head, manifest, tail].join(" # = MANIFEST =\n") 135 | File.open(gemspec_file, 'w') { |io| io.write(spec) } 136 | puts "Updated #{gemspec_file}" 137 | end 138 | 139 | desc "Validate #{gemspec_file}" 140 | task :validate do 141 | libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"] 142 | unless libfiles.empty? 143 | puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir." 144 | exit! 145 | end 146 | unless Dir['VERSION*'].empty? 147 | puts "A `VERSION` file at root level violates Gem best practices." 148 | exit! 149 | end 150 | end 151 | -------------------------------------------------------------------------------- /bin/stars: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'rubygems' 3 | 4 | $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') 5 | 6 | require 'stars' 7 | 8 | Stars::Client.new(ARGV) 9 | -------------------------------------------------------------------------------- /lib/stars.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib]) 2 | 3 | require 'date' 4 | require 'fileutils' 5 | require 'httparty' 6 | require 'keep' 7 | require 'nokogiri' 8 | require 'open-uri' 9 | require 'terminal-table/import' 10 | 11 | require 'stars/client' 12 | require 'stars/config' 13 | require 'stars/post' 14 | 15 | require 'stars/core_ext/string' 16 | 17 | require 'stars/services/service' 18 | require 'stars/services/convore' 19 | require 'stars/services/favstar' 20 | 21 | module Stars 22 | VERSION = '0.5.3' 23 | 24 | def self.config 25 | @config ||= Config.new 26 | end 27 | 28 | def self.services 29 | %w(favstar convore) 30 | end 31 | 32 | def self.installed_services 33 | config.keep.keys 34 | end 35 | 36 | def self.uninstalled_services 37 | services - installed_services 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/stars/client.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # Client is our interface between user and terminal prompt. This does all of 4 | # the heavy-lifting for formatting. 5 | # 6 | module Stars 7 | class Client 8 | 9 | attr_reader :posts 10 | attr_writer :posts 11 | 12 | # Initializes a new Client. 13 | # 14 | # Returns nothing. 15 | def initialize(cmd) 16 | Stars.config.prompt_for_username(cmd[1]) if cmd[0] == 'add' 17 | 18 | system "clear" 19 | puts "★ stars" 20 | 21 | display(cmd[0]) 22 | star_loop 23 | end 24 | 25 | # Run a loop FOREVER until we kill it or we make a selection. 26 | # 27 | # Returns nothing. 28 | def star_loop 29 | selection = '' 30 | while true 31 | puts "Type the number of the post that you want to learn about" 32 | print " (or hit return to view all again, you ego-maniac) >> " 33 | selection = $stdin.gets.chomp 34 | break if ['','q','quit','exit','fuckthis'].include?(selection.downcase) 35 | show(selection) 36 | end 37 | display if selection == '' 38 | end 39 | 40 | # Displays all of the star tables and information we have. 41 | # 42 | # Returns nothing. 43 | def display(service=nil) 44 | Stars.config.prompt_for_service if Stars.installed_services.empty? 45 | 46 | if service && service != 'add' 47 | if Stars.services.include? service 48 | posts = service.constantize.posts 49 | else 50 | puts "Service \"#{service}\" is unknown." 51 | exit 52 | end 53 | else 54 | posts = Stars.installed_services.collect{ |service| 55 | service.constantize.posts }.flatten 56 | end 57 | @posts = Post.filter(posts) 58 | puts print_posts(@posts) 59 | end 60 | 61 | # Show more information about a particular post. 62 | # 63 | # id - the Integer id entered by the user, which we map to a Post 64 | # 65 | # Returns nothing (although does delegate to the Post to show #more). 66 | def show(id) 67 | post = @posts[id.to_i-1] 68 | return puts("\nMake a valid selection. Pretty please?\n") unless post 69 | puts post.more 70 | display 71 | end 72 | 73 | # This does the actual printing of posts. 74 | # 75 | # posts - an Array of Post objects 76 | # 77 | # It loops through the Array of posts and sends them to `terminal-table`. 78 | def print_posts(posts) 79 | table do |t| 80 | t.headings = headings 81 | posts.each_with_index do |post,i| 82 | t << [ 83 | { :value => i+1, :alignment => :right }, 84 | post.service.capitalize, 85 | { :value => post.stars_count, :alignment => :center }, 86 | post.short_name 87 | ] 88 | end 89 | end 90 | end 91 | 92 | # The headings used in the resulting printed table. 93 | # 94 | # This returns an Array of headings. 95 | def headings 96 | [ 97 | '', 98 | 'Service', 99 | 'Stars', 100 | {:value => 'The Hotness', :alignment => :center } 101 | ] 102 | end 103 | 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /lib/stars/config.rb: -------------------------------------------------------------------------------- 1 | module Stars 2 | class Config 3 | 4 | attr_reader :keep 5 | 6 | attr_writer :keep 7 | 8 | def initialize 9 | @keep = Keep.new(config_path) 10 | end 11 | 12 | def config_path 13 | "#{File.expand_path('~')}/.stars.yml" 14 | end 15 | 16 | def username(service) 17 | prompt_for_username(service) 18 | end 19 | 20 | def prompt_for_service 21 | puts "What service do you want to track?" 22 | puts "Your options: #{Stars.uninstalled_services.join(', ')}" 23 | service = $stdin.gets.chomp.downcase 24 | prompt_for_username(service) 25 | end 26 | 27 | def prompt_for_username(service) 28 | return @keep.get(service) if @keep.present?(service) 29 | 30 | if !Stars.uninstalled_services.empty? and !Stars.uninstalled_services.include?(service.downcase) 31 | puts("You need to pick something from: #{Stars.uninstalled_services.join(', ')}") 32 | return exit 33 | end 34 | 35 | puts "What's your username for #{service}?" 36 | username = $stdin.gets.chomp 37 | @keep.set(service,username) 38 | end 39 | 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/stars/core_ext/string.rb: -------------------------------------------------------------------------------- 1 | class String 2 | # Poor man's constantize 3 | def constantize 4 | Stars.const_get(to_s.capitalize) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/stars/post.rb: -------------------------------------------------------------------------------- 1 | module Stars 2 | class Post 3 | attr_reader :url 4 | attr_reader :service 5 | attr_reader :stars_count 6 | attr_reader :date 7 | 8 | attr_writer :name 9 | attr_writer :service 10 | attr_writer :url 11 | attr_writer :stars 12 | attr_writer :stars_count 13 | attr_writer :date 14 | 15 | def initialize(attributes) 16 | @name = attributes[:name] 17 | @url = attributes[:url] 18 | @stars = attributes[:stars] 19 | @stars_count = attributes[:stars_count] 20 | @service = attributes[:service] 21 | @date = attributes[:date] 22 | end 23 | 24 | # The String name of the Post. 25 | # 26 | # This returns the String of the content of the Post (which we just call 27 | # "name"). We also strip whitespace, since it tends to screw up things on 28 | # the command line. 29 | def name 30 | @name.gsub("\n",' ') 31 | end 32 | 33 | # The shorted String version of `name`. 34 | # 35 | # Returns a String of the name truncated at 35 characters. 36 | def short_name 37 | name.size > 35 ? "#{name[0..35]}..." : name 38 | end 39 | 40 | def stars 41 | @stars 42 | end 43 | 44 | def more 45 | service.constantize.more(self) 46 | end 47 | 48 | # Filter an Array of Post objects. 49 | # 50 | # posts - an Array of Post objects to filter 51 | # 52 | # This returns the Array sorted by the 15 most recent stars. 53 | def self.filter(posts) 54 | posts.sort{ |a,b| b.date <=> a.date }[0..14] 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/stars/services/convore.rb: -------------------------------------------------------------------------------- 1 | module Stars 2 | class Convore < Service 3 | 4 | attr_reader :posts 5 | 6 | def name 7 | "convore" 8 | end 9 | 10 | def posts 11 | [Post.new(:name => "Number of stars received", 12 | :stars_count => stars, 13 | :service => name, 14 | :date => DateTime.now, 15 | :url => "https://convore.com/users/#{Stars.config.username('convore')}")] 16 | end 17 | 18 | def html 19 | Nokogiri::HTML(open("https://convore.com/users/#{username}")) 20 | end 21 | 22 | def stars 23 | html.css('.stars-received strong').first.content.to_i 24 | end 25 | 26 | def self.more(post) 27 | return <<-CONVORE 28 | 29 | Convore doesn't have a stars API yet. So we're just scraping your total stars 30 | for now. Kind of a bummer, isn't it? You should probably send @ericflo a tweet 31 | and complain about it. Tell him I didn't send you. 32 | 33 | Anyway, you have #{post.stars_count} stars with Convore right now. Check it: 34 | #{post.url} 35 | 36 | CONVORE 37 | end 38 | 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/stars/services/favstar.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | #{"title"=>"6 stars: Why can't we hardware-accelerate animated gifs? I thought this was the future.", "pubDate"=>"Wed, 16 Feb 2011 05:56:32 +0000", "guid"=>"http://favstar.fm/users/holman/status/37752168152825856?6", "description"=>"Has been faved by 6 people

This tweet   All Recent", "link"=>"http://favstar.fm/users/holman/status/37752168152825856"} 4 | 5 | module Stars 6 | class Favstar < Service 7 | 8 | base_uri 'favstar.fm' 9 | 10 | attr_reader :posts 11 | 12 | def name 13 | "favstar" 14 | end 15 | 16 | def posts 17 | parse_xml 18 | @posts 19 | end 20 | 21 | def xml_feed 22 | self.class.get("/users/#{username}/rss", 23 | :format => :xml)['rss']['channel']['item'] 24 | end 25 | 26 | def parse_xml 27 | @posts = [] 28 | 29 | xml_feed.each do |xml| 30 | @posts << parse_post(xml) 31 | end 32 | end 33 | 34 | def parse_post(xml) 35 | title = xml["title"] 36 | Post.new(:name => parse_title(title), 37 | :url => xml["guid"], 38 | :service => name, 39 | :date => DateTime.parse(xml["pubDate"]), 40 | :stars_count => parse_stars(title)) 41 | end 42 | 43 | # Parse the title from a Favstar RSS title. 44 | # 45 | # title - a Favstar-formatted String (x stars: title here) 46 | # 47 | # This splits on the first colon, and then use everything after that. To 48 | # account for tweets with colons in them, we have to strip the first ": " 49 | # String we find, and then shift the String back two characters. 50 | def parse_title(title) 51 | strip = title.split(':').first 52 | title = title.gsub(strip,'') 53 | title = title[2..-1] if title[0..1] == ": " 54 | title 55 | end 56 | 57 | def parse_stars(title) 58 | title.match(/[\d]+/)[0].to_i 59 | end 60 | 61 | def self.more(post) 62 | # hardcode 17 to strip favstar domain for now 63 | html = get(post.url[17..200], :format => :html) 64 | 65 | output = '' 66 | 67 | Nokogiri::HTML(html).css('div[id^="faved_by_others"] img').collect do |img| 68 | output << " ★ #{img.attributes['alt'].value}\n" 69 | end 70 | 71 | Nokogiri::HTML(html).css('div[id^="rt_by_others"] img').collect do |img| 72 | output << " RT #{img.attributes['alt'].value}\n" 73 | end 74 | 75 | output << "\n" 76 | output << "More info at:\n" 77 | output << " #{post.url}\n\n" 78 | end 79 | 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /lib/stars/services/service.rb: -------------------------------------------------------------------------------- 1 | module Stars 2 | class Service 3 | # Each Service is a party. 4 | include HTTParty 5 | 6 | # Returns a String name of the Service. 7 | # 8 | # We'll display this across Stars to identify this service. 9 | def name ; end 10 | 11 | def username 12 | Stars.config.username(name) 13 | end 14 | 15 | # Returns an Array of Posts. 16 | # 17 | # This method — to be overriden by individual Service classes — returns an 18 | # Array of Post items. Items in each Post should be associated with 19 | # individual Star objects, so we can quickly iterate over a Post's Stars. 20 | def posts ; end 21 | 22 | def self.more(post) ; end 23 | 24 | # Initializes a instance and returns the Posts. 25 | # 26 | # Creates a new instance of the Service and returns an Array of Post 27 | # objects. 28 | def self.posts 29 | self.new.posts 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /stars.gemspec: -------------------------------------------------------------------------------- 1 | ## This is the rakegem gemspec template. Make sure you read and understand 2 | ## all of the comments. Some sections require modification, and others can 3 | ## be deleted if you don't need them. Once you understand the contents of 4 | ## this file, feel free to delete any comments that begin with two hash marks. 5 | ## You can find comprehensive Gem::Specification documentation, at 6 | ## http://docs.rubygems.org/read/chapter/20 7 | Gem::Specification.new do |s| 8 | s.specification_version = 2 if s.respond_to? :specification_version= 9 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 10 | s.rubygems_version = '1.3.5' 11 | 12 | ## Leave these as is they will be modified for you by the rake gemspec task. 13 | ## If your rubyforge_project name is different, then edit it and comment out 14 | ## the sub! line in the Rakefile 15 | s.name = 'stars' 16 | s.version = '0.5.3' 17 | s.date = '2011-07-22' 18 | s.rubyforge_project = 'stars' 19 | 20 | ## Make sure your summary is short. The description may be as long 21 | ## as you like. 22 | s.summary = "Recent stars on your command line." 23 | s.description = "Twitter stars, Convore stars. On the command line." 24 | 25 | ## List the primary authors. If there are a bunch of authors, it's probably 26 | ## better to set the email to an email list or something. If you don't have 27 | ## a custom homepage, consider using your GitHub URL or the like. 28 | s.authors = ["Zach Holman"] 29 | s.email = 'github.com@zachholman.com' 30 | s.homepage = 'https://github.com/holman/stars' 31 | 32 | ## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as 33 | ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb' 34 | s.require_paths = %w[lib] 35 | 36 | ## This sections is only necessary if you have C extensions. 37 | #s.require_paths << 'ext' 38 | #s.extensions = %w[ext/extconf.rb] 39 | 40 | ## If your gem includes any executables, list them here. 41 | s.executables = ["stars"] 42 | s.default_executable = 'stars' 43 | 44 | ## Specify any RDoc options here. You'll want to add your README and 45 | ## LICENSE files to the extra_rdoc_files list. 46 | s.rdoc_options = ["--charset=UTF-8"] 47 | s.extra_rdoc_files = %w[README.markdown LICENSE] 48 | 49 | ## List your runtime dependencies here. Runtime dependencies are those 50 | ## that are needed for an end user to actually USE your code. 51 | s.add_dependency('httparty', [">= 0"]) 52 | s.add_dependency('terminal-table', [">= 0"]) 53 | s.add_dependency('nokogiri', [">= 0"]) 54 | s.add_dependency('keep', ["~> 0.0.3"]) 55 | 56 | ## List your development dependencies here. Development dependencies are 57 | ## those that are only needed during development 58 | s.add_development_dependency('mocha', "~> 0.9.9") 59 | 60 | ## Leave this section as-is. It will be automatically generated from the 61 | ## contents of your Git repository via the gemspec task. DO NOT REMOVE 62 | ## THE MANIFEST COMMENTS, they are used as delimiters by the task. 63 | # = MANIFEST = 64 | s.files = %w[ 65 | Gemfile 66 | Gemfile.lock 67 | LICENSE 68 | README.markdown 69 | Rakefile 70 | bin/stars 71 | lib/stars.rb 72 | lib/stars/client.rb 73 | lib/stars/config.rb 74 | lib/stars/core_ext/string.rb 75 | lib/stars/post.rb 76 | lib/stars/services/convore.rb 77 | lib/stars/services/favstar.rb 78 | lib/stars/services/service.rb 79 | stars.gemspec 80 | test/examples/stars.yml 81 | test/helper.rb 82 | test/test_client.rb 83 | test/test_config.rb 84 | test/test_favstar.rb 85 | test/test_post.rb 86 | ] 87 | # = MANIFEST = 88 | 89 | ## Test files will be grabbed from the file list. Make sure the path glob 90 | ## matches what you actually use. 91 | s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ } 92 | end 93 | -------------------------------------------------------------------------------- /test/examples/stars.yml: -------------------------------------------------------------------------------- 1 | favstar: holman 2 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | require 'test/unit' 4 | 5 | begin 6 | require 'rubygems' 7 | require 'redgreen' 8 | require 'leftright' 9 | rescue LoadError 10 | end 11 | 12 | require 'mocha' 13 | 14 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 15 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 16 | 17 | require 'stars' 18 | 19 | def global_setup 20 | Stars::Config.any_instance.stubs(:config_path). 21 | returns("test/examples/stars.yml") 22 | end 23 | -------------------------------------------------------------------------------- /test/test_client.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | module Kernel 4 | def system(s) ; end 5 | def puts(s) ; end 6 | end 7 | 8 | class TestClient < Test::Unit::TestCase 9 | 10 | def setup 11 | global_setup 12 | end 13 | 14 | def test_add_service 15 | Stars.config.expects(:prompt_for_username).with('favstar').once 16 | Stars::Client.any_instance.stubs(:display) 17 | Stars::Client.any_instance.stubs(:star_loop) 18 | Stars::Client.new(%w(add favstar)) 19 | end 20 | 21 | def test_show_only_one_service 22 | Stars::Favstar.expects(:posts).returns([]) 23 | Stars::Client.any_instance.stubs(:star_loop) 24 | Stars::Client.new(['favstar']) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/test_config.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class TestConfig < Test::Unit::TestCase 4 | 5 | def setup 6 | global_setup 7 | @config = Stars::Config.new 8 | end 9 | 10 | def test_yaml_load 11 | assert_equal 'holman', @config.username('favstar') 12 | end 13 | 14 | def test_prompt_for_username_existing_service 15 | @config.keep.stubs(:present?).returns(true) 16 | Keep.any_instance.expects(:get) 17 | @config.prompt_for_username("favstar") 18 | end 19 | 20 | def test_add_username_to_nonexistent_service 21 | Stars::Config.any_instance.expects(:exit) 22 | @config.prompt_for_username("trololol") 23 | end 24 | 25 | def test_config_path 26 | assert @config.config_path.kind_of?(String) 27 | end 28 | 29 | end 30 | -------------------------------------------------------------------------------- /test/test_favstar.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class TestFavstar < Test::Unit::TestCase 4 | 5 | def setup 6 | global_setup 7 | @favstar = Stars::Favstar.new 8 | end 9 | 10 | def test_parse_title 11 | title = "6 stars: Zach Holman is attractive and handsome." 12 | parsed_title = @favstar.parse_title(title) 13 | assert_equal "Zach Holman is attractive and handsome.", parsed_title 14 | end 15 | 16 | def test_parse_title_with_colons 17 | title = "6 stars: Zach Holman is: attractive and handsome." 18 | parsed_title = @favstar.parse_title(title) 19 | assert_equal "Zach Holman is: attractive and handsome.", parsed_title 20 | end 21 | 22 | def test_parse_stars 23 | title = "6 stars: Zach Holman is attractive and handsome." 24 | parsed_stars = @favstar.parse_stars(title) 25 | assert_equal 6, parsed_stars 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /test/test_post.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class TestPost < Test::Unit::TestCase 4 | 5 | def setup 6 | global_setup 7 | @post = Stars::Post.new(:name => 'post') 8 | end 9 | 10 | def test_short_name 11 | s = "" 12 | 36.times { s << "x" } 13 | @post.name = s 14 | assert_equal true, @post.short_name.include?('...') 15 | end 16 | 17 | def test_full_short_name 18 | s = "" 19 | 10.times { s << "x" } 20 | @post.name = s 21 | assert_equal false, @post.short_name.include?('...') 22 | end 23 | 24 | def test_moar 25 | @post.stubs(:service).returns("Favstar") 26 | Stars::Favstar.expects(:more).returns("favstar more") 27 | assert_equal "favstar more", @post.more 28 | end 29 | 30 | end 31 | --------------------------------------------------------------------------------