rst 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  *.rbc
3
3
  .bundle
4
4
  .config
5
+ .rvmrc
5
6
  .DS_Store
6
7
  .yardoc
7
8
  coverage
File without changes
data/README.md CHANGED
@@ -20,6 +20,8 @@ Current Functionality
20
20
  ---------------------
21
21
 
22
22
  * Read rstat.us world statuses without being logged in.
23
+ * Read a particular user's statuses without being logged in.
24
+ * Search for users whose username contains a pattern.
23
25
 
24
26
  Future functionality
25
27
  --------------------
data/bin/rst CHANGED
@@ -1,49 +1,41 @@
1
1
  #!/usr/bin/env ruby
2
- # 1.9 adds realpath to resolve symlinks; 1.8 doesn't
3
- # have this method, so we add it so we get resolved symlinks
4
- # and compatibility
5
- unless File.respond_to? :realpath
6
- class File #:nodoc:
7
- def self.realpath path
8
- return realpath(File.readlink(path)) if symlink?(path)
9
- path
10
- end
11
- end
12
- end
13
- $: << File.expand_path(File.dirname(File.realpath(__FILE__)) + '/../lib')
14
-
15
- require 'gli'
16
2
  require 'rst'
3
+ require 'optparse'
17
4
 
18
- include GLI
5
+ options = {}
19
6
 
20
- program_desc 'A command line interface for rstat.us'
7
+ rst_parser = OptionParser.new do |opts|
8
+ opts.banner = "A command line interface for rstat.us.
21
9
 
22
- version Rst::VERSION
10
+ usage: rst [options] command
23
11
 
24
- desc 'Display the version and exit.'
25
- switch [:v, :version]
12
+ Commands:
13
+ help [COMMAND] Get more help for a partiular command.
14
+ user [USERNAME] See one particular user's updates.
15
+ world See the latest updates that anyone has made.
16
+ users-search [PATTERN] Search for usernames that match the pattern.
26
17
 
27
- desc 'Get the latest statuses from the whole rstat.us world'
28
- command :world do |c|
29
- c.action do |global_options, options, args|
30
- puts Rst::CLI.world.join("\n\n")
18
+ Options:"
19
+
20
+ opts.on("-n NUMBER", "The number of statuses to show") do |n|
21
+ options[:num] = n.to_i
31
22
  end
32
- end
33
23
 
34
- pre do |global, command, options, args|
35
- if global[:v]
24
+ opts.on_tail("-h", "--help", "Show this message") do
25
+ puts opts
26
+ exit
27
+ end
28
+
29
+ opts.on_tail("-v", "--version", "Show version") do
36
30
  puts "version #{Rst::VERSION}"
37
- exit! 0
31
+ exit
38
32
  end
39
- true
40
- end
41
33
 
42
- post do |global, command, options, args|
43
34
  end
44
35
 
45
- on_error do |exception|
46
- true
36
+ rst_parser.parse!
37
+ if ARGV.empty?
38
+ puts rst_parser.help
39
+ else
40
+ Rst::CLI.run(options, ARGV)
47
41
  end
48
-
49
- exit GLI.run(ARGV)
@@ -22,5 +22,5 @@ Feature: Help text
22
22
  |--help|
23
23
  And the output should contain:
24
24
  """
25
- usage: rst [global options] command
25
+ usage: rst [options] command
26
26
  """
@@ -0,0 +1,14 @@
1
+ Feature: read [username]
2
+ In order to read one user's updates on rstat.us
3
+ I want `rst user [username]` to return all updates by just that user
4
+ So that I can see what they are up to and if I want to follow them or not
5
+
6
+ Scenario: read a particular user
7
+ When I run `rst user carols10cents`
8
+ Then the exit status should be 0
9
+ And the output should contain 20 updates
10
+
11
+ Scenario: no argument
12
+ When I run `rst user`
13
+ Then the exit status should be 1
14
+ And the output should contain "Username is required."
@@ -4,7 +4,20 @@ Then /^the output should contain (\d+) updates$/ do |num|
4
4
  num = num.to_i
5
5
 
6
6
  lines = all_output.split("\n")
7
- non_blank_lines = lines.select{ |line| line.match(/\S+: \S+/) }
7
+ update_lines = lines.select{ |line| line.match(/\S+: \S+/) }
8
8
 
9
- non_blank_lines.size.should == num
9
+ update_lines.size.should == num
10
10
  end
11
+
12
+ Then /^the output should contain (\d+) users$/ do |num|
13
+ num = num.to_i
14
+
15
+ lines = all_output.split("\n")
16
+ user_lines = lines.select{ |line| line.match(/\S+ \(.+\): \S+/) }
17
+
18
+ user_lines.size.should == num
19
+ end
20
+
21
+ Then /^the output should be the version$/ do
22
+ all_output.should match(/^version \d+\.\d+.\d+$/)
23
+ end
@@ -8,7 +8,7 @@ Before do
8
8
  @puts = true
9
9
  @original_rubylib = ENV['RUBYLIB']
10
10
  ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
11
- @aruba_timeout_seconds = 5
11
+ @aruba_timeout_seconds = 10
12
12
  end
13
13
 
14
14
  After do
@@ -0,0 +1,19 @@
1
+ Feature: users-search [pattern]
2
+ If I don't know someone's username exactly
3
+ I want to be able to look it up with a pattern
4
+
5
+ Scenario: search that returns results
6
+ When I run `rst users-search carols10cent`
7
+ Then the exit status should be 0
8
+ And the output should contain 2 users
9
+
10
+ Scenario: search that does not return results
11
+ When I run `rst users-search carols10centz`
12
+ Then the exit status should be 0
13
+ And the output should contain 0 users
14
+ And the output should contain "No users that match."
15
+
16
+ Scenario: no argument
17
+ When I run `rst users-search`
18
+ Then the exit status should be 1
19
+ And the output should contain "Username search pattern is required."
@@ -0,0 +1,9 @@
1
+ Feature: Version
2
+ In order to know what version of rst I'm using
3
+ I want rst to print that out
4
+ So I don't have to look it up in the code
5
+
6
+ Scenario: rst --version
7
+ When I run `rst --version`
8
+ Then the exit status should be 0
9
+ And the output should be the version
@@ -6,4 +6,14 @@ Feature: world
6
6
  Scenario: default world
7
7
  When I run `rst world`
8
8
  Then the exit status should be 0
9
- And the output should contain 20 updates
9
+ And the output should contain 20 updates
10
+
11
+ Scenario: more updates
12
+ When I run `rst world -n 35`
13
+ Then the exit status should be 0
14
+ And the output should contain 35 updates
15
+
16
+ Scenario: fewer updates
17
+ When I run `rst world -n 5`
18
+ Then the exit status should be 0
19
+ And the output should contain 5 updates
data/lib/rst.rb CHANGED
@@ -2,6 +2,7 @@ require "rst/version"
2
2
  require "rst/cli"
3
3
  require "rst/status"
4
4
  require "rst/client"
5
+ require "rst/user"
5
6
 
6
7
  module Rst
7
8
 
@@ -2,16 +2,57 @@ module Rst
2
2
  module CLI
3
3
  extend self
4
4
 
5
- def world(num = 20)
5
+ def run(options, args = [])
6
+ command = args[0]
7
+ rest_of_args = args[1, args.length]
8
+
9
+ results = send(command.gsub(/-/, "_"), options, rest_of_args)
10
+
11
+ puts results.join("\n\n")
12
+ rescue Exception => e
13
+ puts e.message
14
+ exit 1
15
+ end
16
+
17
+ def world(params = {}, args = [])
18
+ statuses(:messages_all, params)
19
+ end
20
+
21
+ def user(params = {}, args = [])
22
+ username = args[0]
23
+ raise "Username is required." unless username
24
+ statuses(:messages_user, params.merge(:username => username))
25
+ end
26
+
27
+ def users_search(params = {}, args = [])
28
+ search_pattern = args[0]
29
+ raise "Username search pattern is required." unless search_pattern
30
+ users = Rst::Client.users_search(:pattern => search_pattern)
31
+ if users.empty?
32
+ ["No users that match."]
33
+ else
34
+ users
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def statuses(which, params = {})
6
41
  statuses = []
7
- page = 0
42
+ page = 0
43
+ num = params[:num] || 20
8
44
 
9
45
  while statuses.size < num do
10
46
  page += 1
11
- statuses.concat Rst::Client.messages_all(:page => page)
47
+ messages = Rst::Client.send(which, params.merge(:page => page))
48
+
49
+ break if messages.empty?
50
+
51
+ statuses.concat messages
12
52
  end
13
53
 
14
54
  statuses.take(num)
15
55
  end
56
+
16
57
  end
17
58
  end
@@ -5,29 +5,100 @@ module Rst
5
5
  module Client
6
6
  extend self
7
7
 
8
- def base_uri
9
- "http://rstat.us"
10
- end
11
-
12
8
  def messages_all(params = {:page => 1})
13
- root_response = Nokogiri::HTML.parse(
14
- Typhoeus::Request.get(base_uri).body
9
+ messages_all_path = find_a_in(root_response, :rel => "messages-all")
10
+ messages_all_uri = resolve_relative_uri(
11
+ :relative => messages_all_path,
12
+ :base => base_uri
15
13
  )
16
14
 
17
- link = root_response.xpath(
18
- "//a[contains(@rel, 'messages-all')]"
19
- ).first
20
-
21
- url = (URI(base_uri) + URI(link["href"])).to_s
15
+ all_response = get_body(messages_all_uri)
16
+ current_uri = messages_all_uri
22
17
 
23
- all_response = Nokogiri::HTML.parse(
24
- Typhoeus::Request.get(url).body
25
- )
18
+ (params[:page] - 1).times do
19
+ next_page_path = find_a_in(all_response, :rel => "next")
20
+ current_uri = resolve_relative_uri(
21
+ :relative => next_page_path,
22
+ :base => current_uri
23
+ )
24
+ all_response = get_body(current_uri)
25
+ end
26
26
 
27
27
  messages = all_response.css("div#messages ul.all li").map { |li|
28
28
  Rst::Status.parse(li)
29
29
  }
30
+ end
31
+
32
+ def messages_user(params = {})
33
+ search_results = users_search(:pattern => params[:username])
34
+
35
+ result = search_results.detect { |sr|
36
+ sr.username.match(/^#{params[:username]}$/i)
37
+ }
38
+
39
+ user_uri = resolve_relative_uri(
40
+ :relative => result.path,
41
+ :base => base_uri
42
+ )
43
+
44
+ user_response = get_body(user_uri)
45
+
46
+ message_nodes = user_response.css("div#messages ul.messages-user li")
47
+ message_nodes.map { |li| Rst::Status.parse(li) }
48
+ end
49
+
50
+ def users_search(params = {})
51
+ users_search_path = find_a_in(root_response, :rel => "users-search")
52
+ users_search_uri = resolve_relative_uri(
53
+ :relative => users_search_path,
54
+ :base => base_uri
55
+ )
56
+
57
+ users_search_response = get_body(users_search_uri)
58
+
59
+ form = users_search_response.css("form.users-search").first
60
+ search_uri = resolve_relative_uri(
61
+ :relative => form["action"],
62
+ :base => users_search_uri
63
+ )
30
64
 
65
+ user_lookup_query = "#{search_uri}?search=#{params[:pattern]}"
66
+
67
+ user_lookup_response = get_body(user_lookup_query)
68
+
69
+ search_results = user_lookup_response.css("div#users ul.search li.user")
70
+ search_results.map { |li| Rst::User.parse(li) }
71
+ end
72
+
73
+ private
74
+
75
+ def find_a_in(html, params = {})
76
+ raise "no rel specified" unless params[:rel]
77
+
78
+ link = html.xpath(
79
+ ".//a[contains(@rel, '#{params[:rel]}')]"
80
+ ).first["href"]
81
+ end
82
+
83
+ def root_response
84
+ get_body(base_uri)
85
+ end
86
+
87
+ def resolve_relative_uri(params = {})
88
+ raise "no relative uri specified" unless params[:relative]
89
+ raise "no base uri specified" unless params[:base]
90
+
91
+ (URI(params[:base]) + URI(params[:relative])).to_s
92
+ end
93
+
94
+ def base_uri
95
+ "http://rstat.us"
96
+ end
97
+
98
+ def get_body(uri)
99
+ Nokogiri::HTML.parse(
100
+ Typhoeus::Request.get(uri).body
101
+ )
31
102
  end
32
103
 
33
104
  def hydra
@@ -0,0 +1,49 @@
1
+ module Rst
2
+ class User
3
+ attr_reader :username, :full_name, :description, :path
4
+
5
+ def initialize(params = {})
6
+ @username = cleanup_whitespace(params[:username])
7
+ @full_name = cleanup_whitespace(params[:full_name])
8
+ @description = cleanup_whitespace(params[:description])
9
+ @path = cleanup_whitespace(params[:path])
10
+ end
11
+
12
+ def to_s
13
+ "#{@username} (#{display_full_name}): #{display_description}"
14
+ end
15
+
16
+ def display_full_name
17
+ if @full_name.nil? || @full_name == ""
18
+ "No full name"
19
+ else
20
+ @full_name
21
+ end
22
+ end
23
+
24
+ def display_description
25
+ if @description.nil? || @description == ""
26
+ "No bio"
27
+ else
28
+ @description
29
+ end
30
+ end
31
+
32
+ def self.parse(li)
33
+ new(
34
+ :username => li.css("span.user-text").text,
35
+ :full_name => li.css("span.user-name").text,
36
+ :description => li.css("span.description").text,
37
+ :path => li.xpath(".//a[contains(@rel, 'user')]").first["href"]
38
+ )
39
+ end
40
+
41
+ private
42
+
43
+ def cleanup_whitespace(html_text)
44
+ if html_text
45
+ html_text.gsub(/\n/, ' ').squeeze(" ").strip
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,3 +1,3 @@
1
1
  module Rst
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -21,7 +21,6 @@ Gem::Specification.new do |gem|
21
21
  gem.add_development_dependency('rake','~> 0.9.2')
22
22
  gem.add_development_dependency('vcr', '~> 2.1.1')
23
23
 
24
- gem.add_dependency('gli', '~> 1.6.0')
25
24
  gem.add_dependency('typhoeus', '~> 0.3.3')
26
25
  gem.add_dependency('nokogiri', '~> 1.5.2')
27
26
  end