birdwatcher 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +481 -0
  7. data/Rakefile +10 -0
  8. data/bin/console +42 -0
  9. data/birdwatcher.gemspec +40 -0
  10. data/data/english_stopwords.txt +319 -0
  11. data/data/top100Kenglishwords.txt +100000 -0
  12. data/db/migrations/001_create_workspaces.rb +11 -0
  13. data/db/migrations/002_create_users.rb +29 -0
  14. data/db/migrations/003_create_statuses.rb +28 -0
  15. data/db/migrations/004_create_mentions.rb +13 -0
  16. data/db/migrations/005_create_mentions_statuses.rb +8 -0
  17. data/db/migrations/006_create_hashtags.rb +11 -0
  18. data/db/migrations/007_create_hashtags_statuses.rb +8 -0
  19. data/db/migrations/008_create_urls.rb +16 -0
  20. data/db/migrations/009_create_statuses_urls.rb +8 -0
  21. data/db/migrations/010_create_klout_topics.rb +10 -0
  22. data/db/migrations/011_create_klout_topics_users.rb +8 -0
  23. data/db/migrations/012_create_influencers.rb +10 -0
  24. data/db/migrations/013_create_influencers_users.rb +8 -0
  25. data/db/migrations/014_create_influencees.rb +10 -0
  26. data/db/migrations/015_create_influencees_users.rb +8 -0
  27. data/exe/birdwatcher +12 -0
  28. data/lib/birdwatcher/command.rb +78 -0
  29. data/lib/birdwatcher/commands/back.rb +15 -0
  30. data/lib/birdwatcher/commands/exit.rb +16 -0
  31. data/lib/birdwatcher/commands/help.rb +60 -0
  32. data/lib/birdwatcher/commands/irb.rb +34 -0
  33. data/lib/birdwatcher/commands/module.rb +106 -0
  34. data/lib/birdwatcher/commands/query.rb +58 -0
  35. data/lib/birdwatcher/commands/query_csv.rb +56 -0
  36. data/lib/birdwatcher/commands/resource.rb +45 -0
  37. data/lib/birdwatcher/commands/run.rb +19 -0
  38. data/lib/birdwatcher/commands/schema.rb +116 -0
  39. data/lib/birdwatcher/commands/set.rb +56 -0
  40. data/lib/birdwatcher/commands/shell.rb +21 -0
  41. data/lib/birdwatcher/commands/show.rb +86 -0
  42. data/lib/birdwatcher/commands/status.rb +114 -0
  43. data/lib/birdwatcher/commands/unset.rb +37 -0
  44. data/lib/birdwatcher/commands/use.rb +25 -0
  45. data/lib/birdwatcher/commands/user.rb +155 -0
  46. data/lib/birdwatcher/commands/workspace.rb +176 -0
  47. data/lib/birdwatcher/concerns/concurrency.rb +25 -0
  48. data/lib/birdwatcher/concerns/core.rb +105 -0
  49. data/lib/birdwatcher/concerns/outputting.rb +114 -0
  50. data/lib/birdwatcher/concerns/persistence.rb +101 -0
  51. data/lib/birdwatcher/concerns/presentation.rb +122 -0
  52. data/lib/birdwatcher/concerns/util.rb +138 -0
  53. data/lib/birdwatcher/configuration.rb +63 -0
  54. data/lib/birdwatcher/configuration_wizard.rb +65 -0
  55. data/lib/birdwatcher/console.rb +201 -0
  56. data/lib/birdwatcher/http_client.rb +164 -0
  57. data/lib/birdwatcher/klout_client.rb +83 -0
  58. data/lib/birdwatcher/kml.rb +125 -0
  59. data/lib/birdwatcher/module.rb +253 -0
  60. data/lib/birdwatcher/modules/statuses/kml.rb +106 -0
  61. data/lib/birdwatcher/modules/statuses/sentiment.rb +77 -0
  62. data/lib/birdwatcher/modules/statuses/word_cloud.rb +205 -0
  63. data/lib/birdwatcher/modules/urls/crawl.rb +138 -0
  64. data/lib/birdwatcher/modules/urls/most_shared.rb +98 -0
  65. data/lib/birdwatcher/modules/users/activity_plot.rb +62 -0
  66. data/lib/birdwatcher/modules/users/import.rb +61 -0
  67. data/lib/birdwatcher/modules/users/influence_graph.rb +93 -0
  68. data/lib/birdwatcher/modules/users/klout_id.rb +62 -0
  69. data/lib/birdwatcher/modules/users/klout_influence.rb +83 -0
  70. data/lib/birdwatcher/modules/users/klout_score.rb +64 -0
  71. data/lib/birdwatcher/modules/users/klout_topics.rb +72 -0
  72. data/lib/birdwatcher/modules/users/social_graph.rb +110 -0
  73. data/lib/birdwatcher/punchcard.rb +183 -0
  74. data/lib/birdwatcher/util.rb +83 -0
  75. data/lib/birdwatcher/version.rb +3 -0
  76. data/lib/birdwatcher.rb +43 -0
  77. data/models/hashtag.rb +8 -0
  78. data/models/influencee.rb +8 -0
  79. data/models/influencer.rb +8 -0
  80. data/models/klout_topic.rb +8 -0
  81. data/models/mention.rb +8 -0
  82. data/models/status.rb +11 -0
  83. data/models/url.rb +8 -0
  84. data/models/user.rb +11 -0
  85. data/models/workspace.rb +26 -0
  86. metadata +405 -0
@@ -0,0 +1,176 @@
1
+ module Birdwatcher
2
+ module Commands
3
+ class Workspace < Birdwatcher::Command
4
+ self.meta = {
5
+ :description => "Manage workspaces",
6
+ :names => %w(workspace workspaces),
7
+ :usage => "workspace [ACTION]"
8
+ }
9
+
10
+ def self.detailed_usage
11
+ <<-USAGE
12
+ Workspaces enable you to segment and manage users and data stored in the database.
13
+ You can use workspaces to create logical separation between different users.
14
+ For example, you may want to create a workspace for a company, a department or
15
+ for a specific topic.
16
+
17
+ There will always be a default workspace with the name #{Birdwatcher::Models::Workspace::DEFAULT_WORKSPACE_NAME.bold} which might be enough
18
+ if you plan to use Birdwatcher for a small group of Twitter users.
19
+
20
+ #{'USAGE:'.bold}
21
+
22
+ #{'List available workspaces:'.bold}
23
+ workspace list
24
+
25
+ #{'Create a new workspace:'.bold}
26
+ workspace create NAME [DESCRIPTION]
27
+
28
+ #{'Switch to a workspace:'.bold}
29
+ workspace use NAME
30
+
31
+ #{'Delete a workspace:'.bold}
32
+ workspace delete NAME
33
+
34
+ #{'Rename a workspace'.bold}
35
+ workspace rename NAME NEW_NAME
36
+ USAGE
37
+ end
38
+
39
+ def run
40
+ if !arguments?
41
+ info("Current workspace: #{current_workspace.name.bold} (database ID: #{current_workspace.id.to_s.bold})")
42
+ return true
43
+ end
44
+ action = arguments.first.downcase
45
+ case action
46
+ when "list"
47
+ list_workspaces
48
+ when "create", "add", "-a"
49
+ create_workspace
50
+ when "rename", "-r"
51
+ rename_workspace
52
+ when "select", "use"
53
+ select_workspace
54
+ when "delete", "destroy", "rm", "-d"
55
+ delete_workspace
56
+ else
57
+ select_workspace(arguments.first)
58
+ end
59
+ end
60
+
61
+ def list_workspaces
62
+ longest_workspace_name = Birdwatcher::Models::Workspace.all.map(&:name).max_by(&:length)
63
+ info("Available workspaces:\n")
64
+ Birdwatcher::Models::Workspace.order(:name).each do |workspace|
65
+ if current_workspace.id == workspace.id
66
+ workspace_name = "*".bold.light_green + " #{workspace.name}"
67
+ else
68
+ workspace_name = " #{workspace.name}"
69
+ end
70
+
71
+ output_formatted(" %-#{longest_workspace_name.bold.length}s \t\t%s\n", workspace_name.bold, workspace.description)
72
+ end
73
+ newline
74
+ end
75
+
76
+ def select_workspace(name = nil)
77
+ name ||= arguments[1]
78
+
79
+ if !name
80
+ error("You must provide a workspace name")
81
+ return false
82
+ end
83
+
84
+ if workspace = Birdwatcher::Models::Workspace.first(:name => name)
85
+ self.current_workspace = workspace
86
+ info("Now using workspace: #{workspace.name.bold}")
87
+ else
88
+ error("There is no workspace with that name")
89
+ end
90
+ end
91
+
92
+ def create_workspace
93
+ name = arguments[1]
94
+ description = arguments[2..-1].to_a.join(" ")
95
+ description = nil unless description
96
+
97
+ if !name
98
+ error("You must provide a workspace name")
99
+ return false
100
+ end
101
+
102
+ if Birdwatcher::Models::Workspace.first(:name => name)
103
+ error("There is already a workspace with that name")
104
+ return false
105
+ end
106
+
107
+ workspace = Birdwatcher::Models::Workspace.create(
108
+ :name => name,
109
+ :description => description
110
+ )
111
+
112
+ info("Created workspace: #{workspace.name.bold}")
113
+ self.current_workspace = workspace
114
+ end
115
+
116
+ def rename_workspace
117
+ old_name = arguments[1]
118
+ new_name = arguments[2]
119
+
120
+ if !old_name || !new_name
121
+ error("You must provide workspace name and new name")
122
+ return false
123
+ end
124
+
125
+ if old_name == Birdwatcher::Models::Workspace::DEFAULT_WORKSPACE_NAME
126
+ error("Default workspace cannot be renamed")
127
+ return false
128
+ end
129
+
130
+ if !old_workspace = Birdwatcher::Models::Workspace.first(:name => old_name)
131
+ error("There is no workspace named #{old_name.bold}")
132
+ return false
133
+ end
134
+
135
+ if Birdwatcher::Models::Workspace.first(:name => new_name)
136
+ error("There is already a workspace named #{new_name.bold}")
137
+ return false
138
+ end
139
+
140
+ old_workspace.update(:name => new_name)
141
+ if old_workspace.id == current_workspace.id
142
+ self.current_workspace = old_workspace
143
+ end
144
+
145
+ info("Workspace #{old_name.bold} renamed to #{new_name.bold}")
146
+ end
147
+
148
+ def delete_workspace
149
+ name = arguments[1]
150
+
151
+ if !name
152
+ error("You must provide a workspace name")
153
+ return false
154
+ end
155
+
156
+ if workspace = Birdwatcher::Models::Workspace.first(:name => name)
157
+ return unless confirm("Are you sure you want to delete #{name.bold} and all associated data?")
158
+ workspace.destroy
159
+ info("Deleted workspace: #{workspace.name.bold}")
160
+ if workspace.default_workspace?
161
+ self.current_workspace = Birdwatcher::Models::Workspace.create_default_workspace!
162
+ return
163
+ end
164
+ if current_workspace.id == workspace.id
165
+ self.current_workspace = Birdwatcher::Models::Workspace.first(
166
+ :name => Birdwatcher::Models::Workspace::DEFAULT_WORKSPACE_NAME
167
+ )
168
+ end
169
+ else
170
+ error("There is no workspace with that name")
171
+ return false
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,25 @@
1
+ module Birdwatcher
2
+ module Concerns
3
+ module Concurrency
4
+ # The default size of thread pool
5
+ # @private
6
+ DEFAULT_THREAD_POOL_SIZE = 10.freeze
7
+
8
+ def self.included(base)
9
+ base.extend(ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ end
14
+
15
+ # Create a new thread pool
16
+ #
17
+ # @param size [Integer] OPTIONAL: The size of the thread pool (default size if not specified)
18
+ # @return [Thread::Pool]
19
+ # @see https://github.com/meh/ruby-thread#pool
20
+ def thread_pool(size = nil)
21
+ Thread.pool(size || DEFAULT_THREAD_POOL_SIZE)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,105 @@
1
+ module Birdwatcher
2
+ module Concerns
3
+ module Core
4
+
5
+ # Location of the data directory
6
+ # @private
7
+ DATA_DIRECTORY = File.expand_path(
8
+ File.join(File.dirname(__FILE__), "..", "..", "..", "data")
9
+ ).freeze
10
+
11
+ class DataFileNotFoundError < StandardError; end
12
+
13
+ def self.included(base)
14
+ base.extend(ClassMethods)
15
+ end
16
+
17
+ module ClassMethods
18
+ end
19
+
20
+ # Get the current Console instance
21
+ #
22
+ # @return [Birdwatcher::Console]
23
+ def console
24
+ Birdwatcher::Console.instance
25
+ end
26
+
27
+ # Get the currently active workspace model object
28
+ #
29
+ # The current workspace is represented by its Sequel data model and can be
30
+ # used to query for data associated with the workspace.
31
+ #
32
+ # Please read the Sequel gem documentation for more information about how
33
+ # to use the model.
34
+ #
35
+ # @return [Birdwatcher::Models::Workspace] instance of the currently active workspace
36
+ # @see http://sequel.jeremyevans.net/documentation.html
37
+ def current_workspace
38
+ Birdwatcher::Console.instance.current_workspace
39
+ end
40
+
41
+ # Set the current workspace
42
+ #
43
+ # @param workspace [Birdwatcher::Models::Workspace]
44
+ def current_workspace=(workspace)
45
+ Birdwatcher::Console.instance.current_workspace = workspace
46
+ end
47
+
48
+ # Get a Twitter API client
49
+ #
50
+ # The Twitter API is being queried with the Twitter gem which provides an
51
+ # easy and intuitive interface to the API and its data objects. Please see
52
+ # the Twitter gem documentation for information on how to use the Twitter
53
+ # gem.
54
+ #
55
+ # The method will return an instance configured with a random Twitter API
56
+ # keypair from the +~/.birdwatcherrc+ configuration file.
57
+ #
58
+ # @return instance of Twitter::REST::Client
59
+ # @see https://github.com/sferik/twitter
60
+ # @see http://www.rubydoc.info/gems/twitter
61
+ def twitter_client
62
+ Birdwatcher::Console.instance.twitter_client
63
+ end
64
+
65
+ # Get a Klout API client
66
+ #
67
+ # The Klout API provides information about Twitter users such as their
68
+ # general "social score", topics of interest and social influence graph.
69
+ #
70
+ # The method will return an instance configured with a random API key from
71
+ # the +~/.birdwatcherrc+ configuration file.
72
+ #
73
+ # @return [Birdwatcher::KloutClient]
74
+ # @see https://klout.com/s/developers/v2
75
+ def klout_client
76
+ Birdwatcher::Console.instance.klout_client
77
+ end
78
+
79
+ # Get the raw database client instance
80
+ #
81
+ # The raw database client object can be used to execute raw SQL queries
82
+ # against the configured database, however the {current_workspace} method
83
+ # should be used whenever possible to execute SQL queries through the
84
+ # current workspace's {Sequel::Model} instance instead. This ensures that
85
+ # the data returned is isolated to the current workspace.
86
+ #
87
+ # @return [Sequel::Database]
88
+ def database
89
+ Birdwatcher::Console.instance.database
90
+ end
91
+
92
+ # Get the contents of a data file
93
+ #
94
+ # @param name [String] file name to read
95
+ #
96
+ # @return contents of file in Birdwatcher's data directory
97
+ # @raise [Birdwatcher::Concerns::Core::DataFileNotFoundError] if the file doesn't exist
98
+ def read_data_file(name)
99
+ path = File.join(DATA_DIRECTORY, name)
100
+ fail(DataFileNotFoundError, "File #{name} was not found in data directory") unless File.exists?(path)
101
+ File.read(path)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,114 @@
1
+ module Birdwatcher
2
+ module Concerns
3
+ module Outputting
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ end
10
+
11
+ # Output data to the console
12
+ #
13
+ # Simply outputs the given data to the console.
14
+ #
15
+ # For more convenient and consistant outputting, see the {info}, {task},
16
+ # {error}, {warn} and {fatal} methods.
17
+ def output(data)
18
+ Birdwatcher::Console.instance.output(data)
19
+ end
20
+
21
+ # Output formatted data to the console
22
+ #
23
+ # Outputs data with +printf+ formatting.
24
+ #
25
+ # @example
26
+ # output_formatted("%-15s %s\n", title, description)
27
+ #
28
+ # @param *args Args to be passed
29
+ def output_formatted(*args)
30
+ Birdwatcher::Console.instance.output_formatted(*args)
31
+ end
32
+
33
+ # Output a newline to the console
34
+ #
35
+ # Used for consistant spacing in console output
36
+ def newline
37
+ Birdwatcher::Console.instance.newline
38
+ end
39
+
40
+ # Output a line to the console
41
+ #
42
+ # Used for consistant spacing and separation between console output
43
+ def line_separator
44
+ Birdwatcher::Console.instance.line_separator
45
+ end
46
+
47
+ # Output an informational message to the console
48
+ #
49
+ # @param message [String] Message to display
50
+ #
51
+ # Formats the message as an informational message
52
+ def info(message)
53
+ Birdwatcher::Console.instance.info(message)
54
+ end
55
+
56
+ # Output an informational message to the console that reports when a
57
+ # longer-running task is done.
58
+ #
59
+ # @param message [String] Message to display
60
+ # @param fatal [Boolean] OPTIONAL if an exception is raised, treat it as a fatal error
61
+ # @param block The code block to yield
62
+ #
63
+ # @example performing a long-running task
64
+ # task("Performing a long, time consuming task...") do
65
+ # long_running_task
66
+ # end
67
+ def task(message, fatal = false, &block)
68
+ Birdwatcher::Console.instance.task(message, fatal, &block)
69
+ end
70
+
71
+ # Output an error message to the console
72
+ #
73
+ # @param message [String] Message to display
74
+ #
75
+ # Formats the message as an error message
76
+ def error(message)
77
+ Birdwatcher::Console.instance.error(message)
78
+ end
79
+
80
+ # Output a warning message to the console
81
+ #
82
+ # @param message [String] Message to display
83
+ #
84
+ # Formats the message as a warning message
85
+ def warn(message)
86
+ Birdwatcher::Console.instance.warn(message)
87
+ end
88
+
89
+ # Output a fatal message to the console
90
+ #
91
+ # @param message [String] Message to display
92
+ #
93
+ # Formats the message as a fatal message
94
+ def fatal(message)
95
+ Birdwatcher::Console.instance.fatal(message)
96
+ end
97
+
98
+ # Ask the user for confirmation
99
+ #
100
+ # @param question [String] Yes/No question to ask the user
101
+ #
102
+ # Waits for the user to answer Yes or No to a question. Useful for making
103
+ # the user confirm destructive actions before executing them.
104
+ #
105
+ # @example make user confirm division by zero
106
+ # if confirm("Do you really want divide by zero?")
107
+ # 0 / 0
108
+ # end
109
+ def confirm(question)
110
+ HighLine.agree("#{question} (y/n) ")
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,101 @@
1
+ module Birdwatcher
2
+ module Concerns
3
+ module Persistence
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ end
10
+
11
+ # Save a Twitter status to the database
12
+ #
13
+ # @param status [Twitter::Tweet]
14
+ # @param user [Birdwatcher::Models::User] Author of status
15
+ #
16
+ # The status will be linked to the current workspace. All URLs, hashtags
17
+ # and mentions will automatically be extracted and saved as separate models.
18
+ #
19
+ # @return [Birdwatcher::Models::Status]
20
+ def save_status(status, user)
21
+ current_workspace = Birdwatcher::Console.instance.current_workspace
22
+ db_status = current_workspace.add_status(
23
+ :user_id => user.id,
24
+ :twitter_id => status.id.to_s,
25
+ :text => Birdwatcher::Util.strip_control_characters(Birdwatcher::Util.unescape_html(status.text)),
26
+ :source => Birdwatcher::Util.strip_control_characters(Birdwatcher::Util.strip_html(status.source)),
27
+ :retweet => status.retweet?,
28
+ :geo => status.geo?,
29
+ :favorite_count => status.favorite_count,
30
+ :retweet_count => status.retweet_count,
31
+ :possibly_sensitive => status.possibly_sensitive?,
32
+ :lang => status.lang,
33
+ :posted_at => status.created_at,
34
+ )
35
+ if status.geo? && status.geo.coordinates
36
+ db_status.longitude = status.geo.coordinates.first
37
+ db_status.latitude = status.geo.coordinates.last
38
+ end
39
+ if status.place?
40
+ db_status.place_type = status.place.place_type
41
+ db_status.place_name = Birdwatcher::Util.strip_control_characters(status.place.name)
42
+ db_status.place_country_code = Birdwatcher::Util.strip_control_characters(status.place.country_code)
43
+ db_status.place_country = Birdwatcher::Util.strip_control_characters(status.place.country)
44
+ end
45
+ db_status.save
46
+ if status.hashtags?
47
+ status.hashtags.each do |hashtag|
48
+ tag = Birdwatcher::Util.strip_control_characters(hashtag.text)
49
+ db_hashtag = current_workspace.hashtags_dataset.first(:tag => tag) || current_workspace.add_hashtag(:tag => tag)
50
+ db_status.add_hashtag(db_hashtag)
51
+ end
52
+ end
53
+ if status.user_mentions?
54
+ status.user_mentions.each do |mention|
55
+ screen_name = Birdwatcher::Util.strip_control_characters(mention.screen_name)
56
+ name = Birdwatcher::Util.strip_control_characters(mention.name)
57
+ db_mention = current_workspace.mentions_dataset.first(:twitter_id => mention.id.to_s) || current_workspace.add_mention(:twitter_id => mention.id.to_s, :screen_name => screen_name, :name => name)
58
+ db_status.add_mention(db_mention)
59
+ end
60
+ end
61
+ if status.urls?
62
+ status.urls.each do |url|
63
+ expanded_url = Birdwatcher::Util.strip_control_characters(url.expanded_url.to_s)
64
+ db_url = current_workspace.urls_dataset.first(:url => expanded_url) || current_workspace.add_url(:url => expanded_url)
65
+ db_status.add_url(db_url)
66
+ end
67
+ end
68
+ db_status
69
+ end
70
+
71
+ # Save a Twitter user to the database
72
+ #
73
+ # @param user [Twitter::User]
74
+ #
75
+ # The user will be linked to the current workspace
76
+ #
77
+ # @return [Birdwatcher::Models::User]
78
+ def save_user(user)
79
+ Birdwatcher::Console.instance.current_workspace.add_user(
80
+ :twitter_id => user.id.to_s,
81
+ :screen_name => Birdwatcher::Util.strip_control_characters(user.screen_name),
82
+ :name => Birdwatcher::Util.strip_control_characters(user.name),
83
+ :location => Birdwatcher::Util.strip_control_characters(user.location),
84
+ :description => Birdwatcher::Util.strip_control_characters(user.description),
85
+ :url => (user.website_urls.first ? Birdwatcher::Util.strip_control_characters(user.website_urls.first.expanded_url.to_s) : nil),
86
+ :profile_image_url => user.profile_image_url_https.to_s,
87
+ :followers_count => user.followers_count,
88
+ :friends_count => user.friends_count,
89
+ :listed_count => user.listed_count,
90
+ :favorites_count => user.favorites_count,
91
+ :statuses_count => user.statuses_count,
92
+ :utc_offset => user.utc_offset,
93
+ :timezone => user.time_zone,
94
+ :geo_enabled => user.geo_enabled?,
95
+ :verified => user.verified?,
96
+ :lang => user.lang
97
+ )
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,122 @@
1
+ module Birdwatcher
2
+ module Concerns
3
+ module Presentation
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ end
10
+
11
+ # Make a user summary output
12
+ #
13
+ # @param user [Birdwatcher::Models::User]
14
+ #
15
+ # @return [String] Short summary of the user
16
+ def make_user_summary_output(user)
17
+ "#{user.name.bold.light_green} (@#{user.screen_name}) #{user.verified ? '*'.bold.light_blue : ''}\n" +
18
+ "Description:".bold + " #{user.description || 'No description'}\n" +
19
+ "Location:".bold + " #{user.location || 'Unknown'}\n" +
20
+ "Followers:".bold + " #{user.followers_count} | " +
21
+ "Following:".bold + " #{user.friends_count} | " +
22
+ "Listed:".bold + " #{user.listed_count} | " +
23
+ "Favorites:".bold + " #{user.favorites_count} | " +
24
+ "Statuses:".bold + " #{user.statuses_count}\n"
25
+ end
26
+
27
+ # Output a user summary to the console
28
+ #
29
+ # @param user [Birdwatcher::Models::User]
30
+ def output_user_summary(user)
31
+ make_user_summary_output(user)
32
+ end
33
+
34
+ # Make user details output
35
+ #
36
+ # @param user [Birdwatcher::Models::User]
37
+ #
38
+ # @return [String] summary and details about a user
39
+ def make_user_details_output(user)
40
+ "#{user.name.bold.light_green} (@#{user.screen_name}) #{user.verified ? '*'.bold.light_blue : ''}\n\n" +
41
+
42
+ "Description:".bold + " #{user.description || 'No description'}\n" +
43
+ " Location:".bold + " #{user.location || 'Unknown'}\n" +
44
+ " Website:".bold + " #{user.url || 'None'}\n" +
45
+ " Timezone:".bold + " #{user.timezone}\n" +
46
+ " Language:".bold + " #{user.lang}\n\n" +
47
+
48
+ "Followers:".bold + " #{user.followers_count}\n" +
49
+ "Following:".bold + " #{user.friends_count}\n" +
50
+ "Favorites:".bold + " #{user.favorites_count}\n" +
51
+ " Statuses:".bold + " #{user.statuses_count}\n\n" +
52
+
53
+ " Added:".bold + " #{time_ago_in_words(user.created_at)}\n" +
54
+ "Updated:".bold + (user.updated_at ? " #{time_ago_in_words(user.updated_at)}" : " Never") + "\n"
55
+ end
56
+
57
+ # Output user details to the console
58
+ #
59
+ # @param user [Birdwatcher::Models::User]
60
+ def output_user_details(user)
61
+ output make_user_details_output(user)
62
+ end
63
+
64
+ # Make status summary output
65
+ #
66
+ # @param status [Birdwatcher::Models::Status]
67
+ #
68
+ # @return [String] short summary of status
69
+ def make_status_summary_output(status)
70
+ "#{status.user.name.bold.light_green} (@#{status.user.screen_name}) #{status.user.verified ? '*'.bold.light_blue : ''} #{status.posted_at.strftime('%b %e, %H:%M')}\n" +
71
+ "#{status.text.bold}\n" +
72
+ "#{'Favorites:'.light_blue} #{status.favorite_count} | " +
73
+ "#{'Retweets:'.light_blue} #{status.retweet_count}\n"
74
+ end
75
+
76
+ # Output status summary to the console
77
+ #
78
+ # @param status [Birdwatcher::Models::Status]
79
+ def output_status_summary(status)
80
+ output make_status_summary_output(status)
81
+ end
82
+
83
+ # Make URL summary output
84
+ #
85
+ # @param url [Hash]
86
+ # @return [String] URL summary
87
+ def make_url_summary_output(url)
88
+ out = "#{(url[:final_url] || url[:url]).bold}\n" +
89
+ "#{'Shares:'.bold} #{url[:count]}\n"
90
+ out += "#{'Title:'.bold} #{url[:title] || 'Unknown'}\n"
91
+ if url[:http_status]
92
+ case url[:http_status]
93
+ when 200..299
94
+ status = url[:http_status].to_s.bold.light_green
95
+ when 400..499
96
+ status = url[:http_status].to_s.bold.light_yellow
97
+ when 500..599
98
+ status = url[:http_status].to_s.bold.light_red
99
+ else
100
+ status = url[:http_status].to_s.bold
101
+ end
102
+ out += "#{'Status Code:'.bold} #{status}\n"
103
+ else
104
+ out += "#{'Status Code:'.bold} Unknown\n"
105
+ end
106
+ out += "#{'Content Type:'.bold} #{url[:content_type] || 'Unknown'}\n"
107
+ out
108
+ end
109
+
110
+ # Page potentially long output to the console
111
+ #
112
+ # @param text [String] Text to page
113
+ #
114
+ # If the text is long, it will be automatically paged with the system's
115
+ # currently configured pager command (usually `less`).
116
+ def page_text(text)
117
+ ::TTY::Pager::SystemPager.new.page(text)
118
+ rescue Errno::EPIPE
119
+ end
120
+ end
121
+ end
122
+ end