birdwatcher 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.
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