wassup 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 941e170c1128cdd099c74de0c068bddabe50ef1921dedcd4f7c278cb6032bd94
4
+ data.tar.gz: b76965eec5063a77ec8d6bca7dee5081a7c380af28b95732298039d1958b5ab6
5
+ SHA512:
6
+ metadata.gz: 8371c29c1c55e96837e07f2c904d27ac22ccec2f848ee2540f9caf0cf4c5d43a746fab12d23a742222dc56c49584eb5a2dead3599c6db86786ed5aa667446bee
7
+ data.tar.gz: 0ad9c8dade67a8a83236c01c1c252bffb96218175eae256268c366f5f7078efa4dbb0d2c1a82fbe0936cfc3a75a52d0aa362814ddb5d6b15b380b825b27e21d3
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ *.swp
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.6.8
6
+ before_install: gem install bundler -v 2.1.4
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at me@joshholtz.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in wassup.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,36 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ wassup (0.1.0)
5
+ curses
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ curses (1.4.2)
11
+ diff-lcs (1.4.4)
12
+ rake (12.3.3)
13
+ rspec (3.10.0)
14
+ rspec-core (~> 3.10.0)
15
+ rspec-expectations (~> 3.10.0)
16
+ rspec-mocks (~> 3.10.0)
17
+ rspec-core (3.10.1)
18
+ rspec-support (~> 3.10.0)
19
+ rspec-expectations (3.10.1)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.10.0)
22
+ rspec-mocks (3.10.2)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.10.0)
25
+ rspec-support (3.10.3)
26
+
27
+ PLATFORMS
28
+ arm64-darwin-21
29
+
30
+ DEPENDENCIES
31
+ rake (~> 12.0)
32
+ rspec (~> 3.0)
33
+ wassup!
34
+
35
+ BUNDLED WITH
36
+ 2.2.3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Josh Holtz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # Wassup
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/wassup`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'wassup'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install wassup
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/wassup. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/wassup/blob/master/CODE_OF_CONDUCT.md).
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
41
+
42
+ ## Code of Conduct
43
+
44
+ Everyone interacting in the Wassup project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/wassup/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "wassup"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/bin/wassup ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'wassup'
4
+ Wassup::App.start(path: ARGV[0])
@@ -0,0 +1,257 @@
1
+ require 'time'
2
+ require 'rest-client'
3
+ require 'json'
4
+ require 'colorize'
5
+
6
+ fastlane_community_prs = []
7
+ fastlane_prs = []
8
+
9
+ #
10
+ # Top Left 1
11
+ #
12
+ add_pane do |pane|
13
+ pane.height = 0.25
14
+ pane.width = 0.35
15
+ pane.top = 0
16
+ pane.left = 0
17
+
18
+ pane.highlight = false
19
+
20
+ pane.title = "Stats: fastlane-community"
21
+
22
+ pane.interval = 1
23
+ pane.content do
24
+ days_1 = 0
25
+ days_7 = 0
26
+ days_30 = 0
27
+ days_60 = 0
28
+ days_365 = 0
29
+
30
+ fastlane_community_prs.each do |pr|
31
+ created_at = pr["created_at"]
32
+ date = Time.parse(created_at)
33
+ days = (Time.now - date).to_i / (24 * 60 * 60)
34
+
35
+ days_1 +=1 if days <= 1
36
+ days_7 +=1 if days <= 7
37
+ days_30 +=1 if days <= 30
38
+ days_60 +=1 if days <= 60
39
+ days_365 +=1 if days <= 365
40
+ end
41
+
42
+ [
43
+ "Opened today: #{days_1}",
44
+ "Opened 7 days: #{days_7}",
45
+ "Opened 30 days: #{days_30}",
46
+ "Opened 60 days: #{days_60}",
47
+ "Opened 365 days: #{days_365}"
48
+ ]
49
+ end
50
+ end
51
+
52
+ #
53
+ # Top Left 2
54
+ #
55
+ add_pane do |pane|
56
+ pane.height = 0.25
57
+ pane.width = 0.35
58
+ pane.top = 0.25
59
+ pane.left = 0
60
+
61
+ pane.highlight = false
62
+
63
+ pane.title = "Stats: fastlane/fastlane"
64
+
65
+ pane.interval = 1
66
+ pane.content do
67
+ days_1 = 0
68
+ days_7 = 0
69
+ days_30 = 0
70
+ days_45 = 0
71
+ days_60 = 0
72
+ days_60_plus = 0
73
+
74
+ fastlane_prs.each do |pr|
75
+ created_at = pr["created_at"]
76
+ date = Time.parse(created_at)
77
+ days = (Time.now - date).to_i / (24 * 60 * 60)
78
+
79
+ days_1 +=1 if days <= 1
80
+ days_7 +=1 if days <= 7
81
+ days_30 +=1 if days <= 30
82
+ days_45 +=1 if days <= 45
83
+ days_60 +=1 if days <= 60
84
+ days_60_plus +=1 if days > 60
85
+ end
86
+
87
+ [
88
+ "Opened today: #{days_1}",
89
+ "Opened 7 days: #{days_7}",
90
+ "Opened 30 days: #{days_30}",
91
+ "Opened 45 days: #{days_45}",
92
+ "Opened 60 days: #{days_60}",
93
+ "More than 60 days: #{days_60_plus}",
94
+ ]
95
+ end
96
+ end
97
+
98
+ #
99
+ # Top Right
100
+ #
101
+ add_pane do |pane|
102
+ pane.height = 0.5
103
+ pane.width = 0.65
104
+ pane.top = 0
105
+ pane.left = 0.35
106
+
107
+ pane.highlight = true
108
+
109
+ pane.title = "Circle CI - fastlane/fastlane"
110
+
111
+ pane.interval = 60 * 5
112
+ pane.content do
113
+ resp = RestClient::Request.execute(
114
+ method: :get,
115
+ url: "https://circleci.com/api/v2/project/github/fastlane/fastlane/pipeline",
116
+ headers: { "Circle-Token": ENV["WASSUP_CIRCLE_CI_API_TOKEN"] }
117
+ )
118
+ json = JSON.parse(resp)
119
+ json["items"].select do |item|
120
+ date = Time.parse(item["updated_at"])
121
+ days = (Time.now - date).to_i / (24 * 60 * 60)
122
+ days < 14
123
+ end.map do |item|
124
+ id = item["id"]
125
+ number = item["number"]
126
+ message = item["vcs"]["commit"]["subject"]
127
+ login = item["trigger"]["actor"]["login"]
128
+
129
+ resp = RestClient::Request.execute(
130
+ method: :get,
131
+ url: "https://circleci.com/api/v2/pipeline/#{id}/workflow",
132
+ headers: { "Circle-Token": ENV["WASSUP_CIRCLE_CI_API_TOKEN"] }
133
+ )
134
+ json = JSON.parse(resp)
135
+ workflow = json["items"].first
136
+ status = workflow["status"]
137
+
138
+ ["#{number} (#{status}) by #{login} - #{message}", workflow]
139
+ end
140
+ end
141
+ pane.selection do |data|
142
+ slug = data["project_slug"]
143
+ pipeline_number = data["pipeline_number"]
144
+ workflow_id = data["id"]
145
+
146
+ url = "https://app.circleci.com/pipelines/#{slug}/#{pipeline_number}/workflows/#{workflow_id}"
147
+ `open #{url}`
148
+ end
149
+ end
150
+
151
+ #
152
+ # Bottom Right
153
+ #
154
+ add_pane do |pane|
155
+ pane.height = 0.5
156
+ pane.width = 0.5
157
+ pane.top = 0.5
158
+ pane.left = 0
159
+
160
+ pane.highlight = true
161
+
162
+ pane.title = "Open PRs - fastlane-community"
163
+
164
+ pane.interval = 60 * 5
165
+ pane.content do
166
+ fastlane_community_prs = []
167
+
168
+ resp = RestClient::Request.execute(
169
+ method: :get,
170
+ url: "https://api.github.com/orgs/fastlane-community/repos",
171
+ user: ENV["WASSUP_GITHUB_USERNAME"],
172
+ password: ENV["WASSUP_GITHUB_ACCESS_TOKEN"]
173
+ )
174
+ json = JSON.parse(resp)
175
+ json.map do |repo|
176
+ name = repo["name"]
177
+ full_name = repo["full_name"]
178
+
179
+ resp = RestClient::Request.execute(
180
+ method: :get,
181
+ url: "https://api.github.com/repos/#{full_name}/pulls",
182
+ user: ENV["WASSUP_GITHUB_USERNAME"],
183
+ password: ENV["WASSUP_GITHUB_ACCESS_TOKEN"]
184
+ )
185
+ json = JSON.parse(resp)
186
+ prs = json.map do |pr|
187
+ fastlane_community_prs << pr
188
+
189
+ number = pr["number"]
190
+ title = pr["title"]
191
+ created_at = pr["created_at"]
192
+
193
+ number_formatted = '%5.5s' % "##{number}"
194
+
195
+ date = Time.parse(created_at)
196
+ days = (Time.now - date).to_i / (24 * 60 * 60)
197
+ days_formatted = '%3.3s' % days.to_s
198
+
199
+ ["#{number_formatted} #{days_formatted}d ago #{title}",pr]
200
+ end
201
+
202
+ {
203
+ title: name,
204
+ content: prs
205
+ }
206
+ end
207
+ end
208
+ pane.selection do |data|
209
+ url = data["html_url"]
210
+ `open #{url}`
211
+ end
212
+ end
213
+
214
+
215
+ #
216
+ # Bottom Right
217
+ #
218
+ add_pane do |pane|
219
+ pane.height = 0.5
220
+ pane.width = 0.5
221
+ pane.top = 0.5
222
+ pane.left = 0.5
223
+
224
+ pane.highlight = true
225
+
226
+ pane.title = "Open PRs - fastlane/fastlane"
227
+
228
+ pane.interval = 60 * 5
229
+ pane.content do
230
+ fastlane_prs = []
231
+
232
+ resp = RestClient::Request.execute(
233
+ method: :get,
234
+ url: "https://api.github.com/repos/fastlane/fastlane/pulls?per_page=100",
235
+ user: ENV["WASSUP_GITHUB_USERNAME"],
236
+ password: ENV["WASSUP_GITHUB_ACCESS_TOKEN"]
237
+ )
238
+ json = JSON.parse(resp)
239
+ json.map do |pr|
240
+ fastlane_prs << pr
241
+
242
+ number = pr["number"]
243
+ title = pr["title"]
244
+ created_at = pr["created_at"]
245
+
246
+ date = Time.parse(created_at)
247
+ days = (Time.now - date).to_i / (24 * 60 * 60)
248
+ days_formatted = '%3.3s' % days.to_s
249
+
250
+ ["##{number} #{days_formatted}d ago #{title}",pr]
251
+ end
252
+ end
253
+ pane.selection do |data|
254
+ url = data["html_url"]
255
+ `open #{url}`
256
+ end
257
+ end
data/lib/wassup/app.rb ADDED
@@ -0,0 +1,87 @@
1
+ require 'curses'
2
+
3
+ module Wassup
4
+ class App
5
+ def self.start(path:)
6
+ Curses.init_screen
7
+ Curses.start_color
8
+ Curses.curs_set(0) # Invisible cursor
9
+ Curses.noecho
10
+
11
+ Curses.stdscr.scrollok true
12
+
13
+ Curses.init_pair(1, 15, 0) #white foreground, black background
14
+ Curses.init_pair(2, 2, 0) # red foreground, black background
15
+ Curses.init_pair(3, 7, 3) # white foreground, yellow background
16
+ Curses.init_pair(4, 0, 15) # white foreground, yellow background
17
+
18
+ app = App.new(path: path)
19
+ end
20
+
21
+ def add_pane
22
+ pane_builder = Wassup::PaneBuilder.new
23
+ yield(pane_builder)
24
+
25
+ number = @panes.size + 1
26
+ pane = Pane.new(
27
+ pane_builder.height,
28
+ pane_builder.width,
29
+ pane_builder.top,
30
+ pane_builder.left,
31
+ title: pane_builder.title,
32
+ highlight: pane_builder.highlight,
33
+ focus_number: number,
34
+ interval: pane_builder.interval,
35
+ content_block: pane_builder.content_block,
36
+ selection_block: pane_builder.selection_block
37
+ )
38
+ pane.focus_handler = @focus_handler
39
+ @panes[number.to_s] = pane
40
+ end
41
+
42
+ def initialize(path:)
43
+ @hidden_pane = nil
44
+ @focused_pane = nil
45
+ @panes = {}
46
+
47
+ @focus_handler = Proc.new do |input|
48
+ if (pane = @panes[input.to_s])
49
+ @focused_pane.focused = false
50
+
51
+ if @focused_pane != pane
52
+ @focused_pane = pane
53
+ @focused_pane.focused = true
54
+ else
55
+ @focused_pane = @hidden_pane
56
+ end
57
+
58
+ true
59
+ end
60
+
61
+ if @focused_pane == @hidden_pane
62
+ true
63
+ else
64
+ false
65
+ end
66
+ end
67
+
68
+ begin
69
+
70
+ @hidden_pane = Pane.new(0, 0, 0, 0, highlight: false, focus_number: 0, interval: nil, content_block: nil, selection_block: nil)
71
+ @hidden_pane.focus_handler = @focus_handler
72
+ @focused_pane = @hidden_pane
73
+
74
+ eval(File.new(path).read)
75
+
76
+ loop do
77
+ @focused_pane.handle_keyboard
78
+ @panes.each do |id, pane|
79
+ pane.refresh()
80
+ end
81
+ end
82
+ ensure
83
+ Curses.close_screen
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,376 @@
1
+ require 'curses'
2
+
3
+ require 'time'
4
+
5
+ module Wassup
6
+ class Pane
7
+ attr_accessor :win
8
+ attr_accessor :subwin
9
+ attr_accessor :data_lines
10
+ attr_accessor :data_objects
11
+ attr_accessor :top
12
+
13
+ attr_accessor :title
14
+
15
+ attr_accessor :focused
16
+ attr_accessor :focus_number
17
+
18
+ attr_accessor :should_box
19
+
20
+ attr_accessor :highlighted_line
21
+
22
+ attr_accessor :highlight
23
+ attr_accessor :virtual_scroll
24
+
25
+ attr_accessor :focus_handler
26
+
27
+ attr_accessor :interval
28
+ attr_accessor :last_refreshed
29
+ attr_accessor :content_block
30
+ attr_accessor :selection_block
31
+
32
+ attr_accessor :selected_view_index
33
+ attr_accessor :all_view_data_objects
34
+
35
+ attr_accessor :win_height, :win_width, :win_top, :win_left
36
+
37
+ def initialize(height, width, top, left, title: nil, highlight: true, focus_number: nil, interval:, content_block:, selection_block:)
38
+ self.win_height = Curses.lines * height
39
+ self.win_width = Curses.cols * width
40
+ self.win_top = Curses.lines * top
41
+ self.win_left = Curses.cols * left
42
+
43
+ self.win = Curses::Window.new(self.win_height, self.win_width, self.win_top, self.win_left)
44
+ self.setup_subwin()
45
+
46
+ self.focused = false
47
+ self.focus_number = focus_number
48
+
49
+ self.highlight = highlight
50
+ self.virtual_scroll = true
51
+
52
+ self.top = 0
53
+ self.data_lines = []
54
+ self.data_objects = []
55
+
56
+ self.selected_view_index = 0
57
+ self.all_view_data_objects = [] # Array of array
58
+
59
+ self.win.refresh
60
+ self.subwin.refresh
61
+
62
+ self.title = title
63
+
64
+ self.interval = interval
65
+ self.content_block = content_block
66
+ self.selection_block = selection_block
67
+ end
68
+
69
+ def setup_subwin
70
+ top_bump = 0
71
+
72
+ unless self.subwin.nil?
73
+ self.subwin.clear()
74
+ self.subwin.close()
75
+ self.subwin = nil
76
+ end
77
+
78
+ if (@all_view_data_objects || []).size > 1
79
+ top_bump = 4
80
+
81
+ view_title = (self.all_view_data_objects[self.selected_view_index] || {})[:title]
82
+ view_title += " " * 100
83
+
84
+ self.win.setpos(2, 2)
85
+ self.win.addstr(view_title[0...self.win.maxx()-3])
86
+
87
+ subtitle = "(#{self.selected_view_index + 1} out of #{self.all_view_data_objects.size})"
88
+ subtitle += " " * 100
89
+ self.win.setpos(3, 2)
90
+ self.win.addstr(subtitle[0...self.win.maxx()-3])
91
+
92
+ self.win.refresh
93
+ end
94
+
95
+ if self.win_height == 0
96
+ self.should_box = false
97
+ self.subwin = self.win.subwin(0, 0, 0, 0)
98
+ else
99
+ self.should_box = true
100
+ self.subwin = self.win.subwin(self.win_height - 2 - top_bump, self.win_width - 4, self.win_top + 1 + top_bump, self.win_left + 2)
101
+ end
102
+ self.subwin.nodelay=true
103
+ self.subwin.idlok(true)
104
+ self.subwin.scrollok(true)
105
+ end
106
+
107
+ def needs_refresh?
108
+ return false if self.content_block.nil?
109
+ return false if self.interval.nil?
110
+ return true if self.last_refreshed.nil?
111
+ return Time.now - self.interval > self.last_refreshed
112
+ end
113
+
114
+ def refresh
115
+ return unless needs_refresh?
116
+
117
+ content = self.content_block.call()
118
+
119
+ return unless content.is_a?(Array)
120
+ return if content.first.nil?
121
+
122
+ if content.first.is_a?(Hash)
123
+ self.all_view_data_objects = content
124
+ else
125
+ self.all_view_data_objects = [
126
+ content
127
+ ]
128
+ end
129
+
130
+ self.load_current_view()
131
+
132
+ self.last_refreshed = Time.now
133
+ end
134
+
135
+ def load_current_view
136
+ self.setup_subwin()
137
+ view_content = self.all_view_data_objects[self.selected_view_index]
138
+
139
+ # this might be bad
140
+ self.highlighted_line = nil
141
+
142
+ require 'pp'
143
+ if view_content.is_a?(Hash) && view_content[:content]
144
+ view_content = view_content[:content]
145
+ end
146
+
147
+ @data_lines = []
148
+ @data_objects = []
149
+ view_content.each do |item|
150
+ if item.is_a?(String)
151
+ @data_objects << item
152
+ self.add_line(item)
153
+ elsif item.is_a?(Array)
154
+ @data_objects << item[1]
155
+ self.add_line(item[0])
156
+ end
157
+ end
158
+ end
159
+
160
+ def title=(title)
161
+ @title = title
162
+ self.update_box()
163
+ self.update_title()
164
+ end
165
+
166
+ def update_title
167
+ title = self.title || "<No Title>"
168
+ full_title = "#{self.focus_number} - #{title}"
169
+
170
+ self.win.setpos(0, 3)
171
+ self.win.addstr(full_title)
172
+ self.win.refresh
173
+ end
174
+
175
+ def focused=(focused)
176
+ @focused = focused
177
+ self.update_box()
178
+ self.update_title()
179
+ self.virtual_reload()
180
+ end
181
+
182
+ def update_box
183
+ return unless self.should_box
184
+
185
+ self.win.attron(self.focused ? Curses.color_pair(2) : Curses.color_pair(1))
186
+ self.win.box()
187
+ self.win.attron(Curses.color_pair(1))
188
+
189
+ self.win.refresh
190
+ end
191
+
192
+ def add_line(text)
193
+ data_lines << text
194
+
195
+ if self.virtual_scroll
196
+ self.virtual_reload
197
+ else
198
+ self.load_thing
199
+ end
200
+ end
201
+
202
+ # Load the file into memory and
203
+ # put the first part on the curses display.
204
+ def load_thing
205
+ self.data_lines[0..self.subwin.maxy-1].each_with_index do |line, idx|
206
+ self.subwin.setpos(idx, 0)
207
+ self.subwin.addstr(line)
208
+ end
209
+ self.subwin.setpos(0, 0)
210
+ self.subwin.refresh
211
+ end
212
+
213
+ def virtual_reload
214
+ return if self.data_lines.nil?
215
+
216
+ self.data_lines[self.top..(self.top+self.subwin.maxy-1)].each_with_index do |line, idx|
217
+
218
+ if self.focused && self.highlight && (idx + self.top) == self.highlighted_line
219
+ self.subwin.attron(Curses.color_pair(4))
220
+ else
221
+ self.subwin.attron(Curses.color_pair(1))
222
+ end
223
+
224
+ short_line = line[0...self.subwin.maxx()-3]
225
+
226
+ self.subwin.setpos(idx, 0)
227
+ self.subwin.addstr(short_line)
228
+ self.subwin.clrtoeol()
229
+
230
+ self.subwin.attron(Curses.color_pair(1))
231
+ end
232
+ self.subwin.refresh
233
+ end
234
+
235
+ def virtual_scroll_down
236
+ self.update_highlight(1)
237
+
238
+ bottom = self.top + self.subwin.maxy
239
+ if bottom < self.data_lines.size
240
+ # only move highlight and dont scroll if near top
241
+ if !self.highlight || self.highlighted_line > highlight_scroll_padding
242
+ self.top += 1
243
+ end
244
+ end
245
+
246
+ self.virtual_reload
247
+ end
248
+
249
+ def highlight_scroll_padding
250
+ return (self.subwin.maxy * 0.3).to_i
251
+ end
252
+
253
+ def virtual_scroll_up
254
+ self.update_highlight(-1)
255
+
256
+ if self.top > 0
257
+ # only move highlight and dont scroll if near bottom
258
+ if !self.highlight || (self.data_lines.size - self.highlighted_line) >= highlight_scroll_padding
259
+ self.top -= 1
260
+ end
261
+ end
262
+
263
+ self.virtual_reload
264
+ end
265
+
266
+ def update_highlight(inc)
267
+ return unless self.highlight
268
+
269
+ prev = self.highlighted_line
270
+
271
+ new = nil
272
+ if prev.nil?
273
+ new = 0
274
+ elsif inc.nil?
275
+ new = nil
276
+ elsif !inc.nil?
277
+ new = prev + inc
278
+ end
279
+
280
+ if new < 0 || new >= self.data_lines.size
281
+ new = prev
282
+ end
283
+
284
+ self.highlighted_line = new
285
+
286
+ return new
287
+ end
288
+
289
+ def scroll_left
290
+ self.selected_view_index -= 1
291
+ if self.selected_view_index < 0
292
+ self.selected_view_index = self.all_view_data_objects.size - 1
293
+ end
294
+
295
+ self.load_current_view
296
+ end
297
+
298
+ def scroll_right
299
+ self.selected_view_index += 1
300
+ if self.selected_view_index >= self.all_view_data_objects.size
301
+ self.selected_view_index = 0
302
+ end
303
+
304
+ self.load_current_view
305
+ end
306
+
307
+ # Scroll the display up by one line.
308
+ def scroll_up
309
+ if self.top > 0
310
+ self.subwin.scrl(-1)
311
+ self.top -= 1
312
+ str = self.data_lines[top]
313
+ if str
314
+ self.subwin.setpos(0, 0)
315
+ self.subwin.addstr(str)
316
+ end
317
+ return true
318
+ else
319
+ return false
320
+ end
321
+ end
322
+
323
+ # Scroll the display down by one line.
324
+ def scroll_down
325
+ if self.top + self.subwin.maxy < self.data_lines.length
326
+ self.subwin.scrl(1)
327
+ self.top += 1
328
+ str = self.data_lines[self.top + self.subwin.maxy - 1]
329
+ if str
330
+ self.subwin.attron(Curses.color_pair(1))
331
+ self.subwin.setpos(self.subwin.maxy - 1, 0)
332
+ self.subwin.addstr(str)
333
+ self.subwin.attron(Curses.color_pair(1))
334
+ end
335
+ return true
336
+ else
337
+ return false
338
+ end
339
+ end
340
+
341
+ def handle_keyboard
342
+ input = self.subwin.getch
343
+
344
+ unless focus_handler.nil?
345
+ handled = focus_handler.call(input)
346
+ return if handled
347
+ end
348
+
349
+ if input == "j"
350
+ if virtual_scroll
351
+ virtual_scroll_down
352
+ else
353
+ scroll_down
354
+ end
355
+ elsif input == "k"
356
+ if virtual_scroll
357
+ virtual_scroll_up
358
+ else
359
+ scroll_up
360
+ end
361
+ elsif input == "h"
362
+ scroll_left
363
+ elsif input == "l"
364
+ scroll_right
365
+ elsif input == "q"
366
+ # TODO: This needs to quit
367
+ # Need to kill the loop or something
368
+ elsif input == 10 # enter
369
+ if !self.selection_block.nil? && !self.highlighted_line.nil?
370
+ data = @data_objects[self.highlighted_line]
371
+ self.selection_block.call(data)
372
+ end
373
+ end
374
+ end
375
+ end
376
+ end
@@ -0,0 +1,24 @@
1
+
2
+ module Wassup
3
+ class PaneBuilder
4
+ attr_accessor :height
5
+ attr_accessor :width
6
+ attr_accessor :top
7
+ attr_accessor :left
8
+
9
+ attr_accessor :highlight
10
+
11
+ attr_accessor :title
12
+
13
+ attr_accessor :interval
14
+ attr_accessor :content_block
15
+ attr_accessor :selection_block
16
+
17
+ def content(&block)
18
+ self.content_block = block
19
+ end
20
+ def selection(&block)
21
+ self.selection_block = block
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module Wassup
2
+ VERSION = "0.1.0"
3
+ end
data/lib/wassup.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "wassup/version"
2
+ require "wassup/app"
3
+ require "wassup/pane"
4
+ require "wassup/pane_builder"
5
+
6
+ module Wassup
7
+ class Error < StandardError; end
8
+ # Your code goes here...
9
+ end
data/wassup.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ require_relative 'lib/wassup/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "wassup"
5
+ spec.version = Wassup::VERSION
6
+ spec.authors = ["Josh Holtz"]
7
+ spec.email = ["me@joshholtz.com"]
8
+
9
+ spec.summary = %q{A scriptable terminal dashboard}
10
+ spec.description = %q{A scriptable terminal dashboard}
11
+ spec.homepage = "https://github.com/joshdholtz/wassup"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = "https://github.com/joshdholtz/wassup"
17
+
18
+ spec.add_runtime_dependency 'curses'
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ end
25
+ spec.bindir = "bin"
26
+ spec.executables = ["wassup"]
27
+ spec.require_paths = ["lib"]
28
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wassup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Josh Holtz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-12-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: curses
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: A scriptable terminal dashboard
28
+ email:
29
+ - me@joshholtz.com
30
+ executables:
31
+ - wassup
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".gitignore"
36
+ - ".rspec"
37
+ - ".travis.yml"
38
+ - CODE_OF_CONDUCT.md
39
+ - Gemfile
40
+ - Gemfile.lock
41
+ - LICENSE.txt
42
+ - README.md
43
+ - Rakefile
44
+ - bin/console
45
+ - bin/setup
46
+ - bin/wassup
47
+ - examples/josh-fastlane/Supfile
48
+ - lib/wassup.rb
49
+ - lib/wassup/app.rb
50
+ - lib/wassup/pane.rb
51
+ - lib/wassup/pane_builder.rb
52
+ - lib/wassup/version.rb
53
+ - wassup.gemspec
54
+ homepage: https://github.com/joshdholtz/wassup
55
+ licenses:
56
+ - MIT
57
+ metadata:
58
+ homepage_uri: https://github.com/joshdholtz/wassup
59
+ source_code_uri: https://github.com/joshdholtz/wassup
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 2.3.0
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubygems_version: 3.2.3
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: A scriptable terminal dashboard
79
+ test_files: []