rpw 1.0.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45b744148f7441c89e4b754ec0b19105e0bfbdb8e9b97e0196d0d6c0ff51266c
4
- data.tar.gz: 8eac7fc7584553d6af5e2c250520ff05844e9243882baceeb133f163cee2901e
3
+ metadata.gz: 83a9031c1e6053a268195bd32096243b33f75328cf27ca79ecd646b4de2b9ed3
4
+ data.tar.gz: 221242a7821edbdbd66e42b6a56a1eb807789e82bc903a61421a6c5d27189b2b
5
5
  SHA512:
6
- metadata.gz: e20a592d2777d2fbd325cae566fe4e49ed9d07eb496c6da31d1dd70010d08baf08827095c4ffec842e7a9cfeb886d755afd632bedf6e9b671fd3d32fd062382e
7
- data.tar.gz: 0b8360255ef4fd5fba85bf982236e44b0f07050a9d45c1f557e047b82d5dd91a243f2c4be96c8202953a3e993c09ecfaec4de9f803c803f038456e952d18cc08
6
+ metadata.gz: 57b5addc24163780426263bed53f08ee9bf71358d1509940d08e6e5d8a4d3b1b0e01010249318e7cd22fdabece0ed66fceea7ea98796d3db2fd1100aa28ff5ff
7
+ data.tar.gz: 02f3b22fcaad4f7d4098b6a5ca49cc577dcab04639fae3fe1019fea073ff0c0b54c918bdc4630ea6429650eef89647d77c6be2aa0d7265414c21236888f61d99
@@ -20,7 +20,7 @@ jobs:
20
20
  fail-fast: true
21
21
  matrix:
22
22
  os: [ ubuntu-20.04, macos-10.15, windows-2019 ]
23
- ruby: [ 2.3, 2.6, 2.7, head ]
23
+ ruby: [ 2.6, 2.7, head ]
24
24
 
25
25
  steps:
26
26
  - name: repo checkout
@@ -33,7 +33,7 @@ jobs:
33
33
 
34
34
  - name: bundle install
35
35
  run: |
36
- bundle install --jobs 4 --retry 3
36
+ gem install bundler && bundle install --jobs 4 --retry 3
37
37
 
38
38
  - name: standardrb
39
39
  run: bundle exec standardrb
data/.gitignore CHANGED
@@ -3,4 +3,5 @@ bundle
3
3
  .rpw_key
4
4
  .rpw_info
5
5
  smoketest
6
- scripts
6
+ scripts
7
+ .DS_Store
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rpw (1.0.0)
4
+ rpw (1.4.0)
5
+ cli-ui
5
6
  excon
6
7
  thor
7
8
  thor-hollaback
@@ -10,7 +11,8 @@ GEM
10
11
  remote: https://rubygems.org/
11
12
  specs:
12
13
  ast (2.4.1)
13
- excon (0.78.0)
14
+ cli-ui (1.5.1)
15
+ excon (0.81.0)
14
16
  hollaback (0.1.0)
15
17
  minitest (5.14.2)
16
18
  parallel (1.20.0)
@@ -38,7 +40,7 @@ GEM
38
40
  standard (0.9.0)
39
41
  rubocop (= 1.2.0)
40
42
  rubocop-performance (= 1.8.1)
41
- thor (1.0.1)
43
+ thor (1.1.0)
42
44
  thor-hollaback (0.2.0)
43
45
  hollaback (~> 0.1.0)
44
46
  thor (>= 0.19.1)
@@ -54,4 +56,4 @@ DEPENDENCIES
54
56
  standard
55
57
 
56
58
  BUNDLED WITH
57
- 2.1.4
59
+ 2.2.3
data/HISTORY.md CHANGED
@@ -1,3 +1,28 @@
1
+ ## 1.4.0
2
+
3
+ * Add `--quizzes` option to `rpw show`, to see quizzes
4
+
5
+ ## 1.3.1
6
+
7
+ * Removed tar verbose mode
8
+
9
+ ## 1.3.0
10
+
11
+ * Added suggested schedules to README
12
+
13
+ ## 1.2.0
14
+
15
+ * Actual release version!
16
+ * Greatly streamlined commands
17
+
18
+ ## 1.1.0
19
+
20
+ * sick colors
21
+
22
+ ## 1.0.1
23
+
24
+ * Pre-launch bugfix
25
+
1
26
  ## 1.0.0
2
27
 
3
28
  * First release!
data/lib/rpw/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ## Installation Requirements
2
2
 
3
- This client assumes you're using Ruby 2.3 or later.
3
+ This client assumes you're using Ruby 2.6 or later.
4
4
 
5
5
  This client assumes you have `tar` installed and available on your PATH.
6
6
 
@@ -11,6 +11,11 @@ The way that the client opens files (using `open` or `xdg-open` depending on pla
11
11
 
12
12
  ## Slack Invite
13
13
 
14
+ The Slack channel is your best resource for questions about Rails Performance
15
+ or other material in the workshop. Nate is almost always monitoring that channel.
16
+
17
+ If you encounter a **bug or other software problem**, please email support@speedshop.co.
18
+
14
19
  If you purchased the Workshop yourself, you will receive a Slack channel invitation
15
20
  shortly. If you are attending the Workshop as part of a group and your license key
16
21
  was provided to you, you need to register your key to get an invite:
@@ -21,33 +26,30 @@ $ rpw key register [YOUR_EMAIL_ADDRESS]
21
26
 
22
27
  Please note you can only register your key once.
23
28
 
24
- The Slack channel is your best resource for questions about Rails Performance
25
- or other material in the workshop. Nate is almost always monitoring that channel.
26
-
27
- If you encounter a **bug or other software problem**, please email support@speedshop.co.
28
-
29
29
  ## Important Commands
30
30
 
31
31
  Here are some important commands for you to know:
32
32
 
33
33
  ```
34
- $ rpw lesson next | Proceed to the next part of the workshop.
35
- $ rpw lesson complete | Mark current lesson as complete.
36
- $ rpw lesson list | List all workshop lessons. Note each lesson is preceded with an ID.
37
- $ rpw lesson download | Download any or all lessons. Use the IDs from "list".
38
- $ rpw lesson show | Show any particular workshop lesson. Use the IDs from "list".
39
- $ rpw progress | Show where you're currently at in the workshop.
34
+ $ rpw next | Proceed to the next part of the workshop.
35
+ $ rpw complete | Mark current lesson as complete.
36
+ $ rpw list | List all workshop lessons. Shows progress.
37
+ $ rpw download | Download all lessons. Useful for offline access.
38
+ $ rpw show | Show any particular workshop lesson.
39
+ $ rpw current | Opens the current lesson.
40
40
  ```
41
41
 
42
- Generally, you'll just be doing a lot of `$ rpw lesson next`!
42
+ Generally, you'll just be doing a lot of `$ rpw next`! It's basically the same thing as `$ rpw complete && rpw show`.
43
+
44
+ #### --no-open
43
45
 
44
- By default, `$ rpw lesson next` will try to open the content it downloads. If you
45
- either don't like this, or for some reason it doesn't work, use `$ rpw lesson next --no-open`.
46
+ By default, `$ rpw next` (and `$ rpw show` and `$ rpw current`) will try to open the content it downloads. If you
47
+ either don't like this, or for some reason it doesn't work, use `$ rpw next --no-open`.
46
48
 
47
49
  ## Working Offline
48
50
 
49
51
  By default, the course will download each piece of content as you progress through
50
- the course. However, you can use `rpw lesson download all` to download all content
52
+ the course. However, you can use `rpw download` to download all content
51
53
  at once, and complete the workshop entirely offline.
52
54
 
53
55
  Videos in this workshop are generally about 100MB each, which means the entire
@@ -55,4 +57,38 @@ course is about a 3 to 4GB download.
55
57
 
56
58
  ## Bugs and Support
57
59
 
58
- If you encounter any problems, please email support@speedshop.co for the fastest possible response.
60
+ If you encounter any problems, please email support@speedshop.co for the fastest possible response.
61
+
62
+ ## Suggested Schedules
63
+
64
+ I've found that self-directed learners are most effective when they set a schedule for themselves. These schedules will work if you are learning alone or in a group.
65
+
66
+ ### 4-Week Schedule
67
+
68
+ This is my recommended schedule. It is designed so that you can do the workshop as "20% time", or as part of a larger performance "sprint".
69
+
70
+ If you follow this schedule, you should be able to complete the workshop in about 4 weeks. I suggest leaving an entire day aside to complete your material for the week, e.g. Mondays. Tuesday through Friday should be used to complete exercises and PR Safaris.
71
+
72
+ * **Week 1**: Modules 1 and 2
73
+ * **Week 2**: Module 3 and 4
74
+ * **Week 3**: Module 5
75
+ * **Week 4**: Module 6
76
+
77
+ ### 1-Week Intensive Schedule
78
+
79
+ For those of you looking to immerse yourselves and only work on this workshop for 8 hours a day for 5 days, this schedule is suggested:
80
+
81
+ * **Day 1**: Modules 1 and 2
82
+ * **Day 2**: Module 3
83
+ * **Day 3**: Module 4
84
+ * **Day 4**: Module 5
85
+ * **Day 5**: Module 6
86
+
87
+ ### 80/20 Schedule
88
+
89
+ Short on time? Here's the 20% of the course content that's the most important:
90
+
91
+ * Module 1
92
+ * Module 5
93
+
94
+ If I had the time to go through a limited amount of the workshop content, I would concentrate exclusively on these two modules.
data/lib/rpw/cli.rb CHANGED
@@ -4,20 +4,16 @@ require "rpw"
4
4
  require "rpw/cli/bannerlord"
5
5
  require "rpw/cli/sub_command_base"
6
6
  require "rpw/cli/key"
7
- require "rpw/cli/lesson"
8
- require "rpw/cli/progress"
7
+ require "cli/ui"
8
+
9
+ CLI::UI::StdoutRouter.enable
9
10
 
10
11
  module RPW
11
12
  class CLI < Thor
12
13
  class_before :check_version
13
- class_before :check_setup
14
14
 
15
15
  desc "key register [EMAIL_ADDRESS]", "Change email registered w/Speedshop"
16
16
  subcommand "key", Key
17
- desc "lesson [SUBCOMMAND]", "View and download lessons"
18
- subcommand "lesson", Lesson
19
- desc "progress [SUBCOMMAND]", "View and set progress"
20
- subcommand "progress", Progress
21
17
 
22
18
  def self.exit_on_failure?
23
19
  true
@@ -28,7 +24,7 @@ module RPW
28
24
  warn_if_already_started
29
25
 
30
26
  print_banner
31
- say "Welcome to the Rails Performance Workshop."
27
+ say "\u{1F48E} Welcome to the Rails Performance Workshop. \u{1F48E}"
32
28
  say ""
33
29
  say "This is rpw, the command line client for this workshop."
34
30
  say ""
@@ -36,27 +32,160 @@ module RPW
36
32
  say "working directory, so it's best to run this client from a new directory"
37
33
  say "that you'll use as your 'scratch space' for working on the Workshop."
38
34
  say ""
39
- say "We will create a handful of new files and folders in the current directory."
40
- return unless yes? "Is this OK? (y/N) (N will quit)"
41
- puts ""
42
- say "We'll also create a .rpw_info file at #{File.expand_path("~/.rpw")} to save your purchase key."
43
- home_dir_ok = yes?("Is this OK? (y/N) (N will create it in the current directory)")
44
- client.directory_setup(home_dir_ok)
45
35
 
46
- key = ask("Your Purchase Key: ")
36
+ ans = ::CLI::UI.confirm "Create files and folders in this directory? (no will quit)"
37
+
38
+ exit(1) unless ans
39
+
40
+ say ""
41
+
42
+ ans = ::CLI::UI::Prompt.ask("Where should we save your course progress?",
43
+ options: [
44
+ "here",
45
+ "my home directory (~/.rpw)"
46
+ ])
47
+
48
+ client.directory_setup((ans == "my home directory (~/.rpw)"))
49
+
50
+ key = ::CLI::UI::Prompt.ask("Your Purchase Key: ")
47
51
 
48
52
  unless client.setup(key)
49
53
  say "That is not a valid key. Please try again."
50
54
  exit(0)
51
55
  end
52
56
 
53
- puts ""
57
+ say ""
54
58
  say "Successfully authenticated with the RPW server and saved your key."
55
- puts ""
59
+ say ""
56
60
  say "Setup complete!"
57
- puts ""
58
- say "To learn how to use this command-line client, consult ./README.md, which we just created."
59
- say "Once you've read that and you're ready to get going: $ rpw lesson next"
61
+ say ""
62
+ say "To learn how to use this command-line client, consult ./README.md,"
63
+ say "which we just created."
64
+ say ""
65
+ say "Once you've read that and you're ready to get going: $ rpw next"
66
+ end
67
+
68
+ desc "next", "Proceed to the next lesson of the workshop"
69
+ option :"no-open", type: :boolean
70
+ def next
71
+ exit_with_no_key
72
+ content = client.next
73
+
74
+ if content.nil?
75
+ RPW::CLI.new.print_banner
76
+ say "Congratulations!"
77
+ say "You have completed the Rails Performance Workshop."
78
+ exit(0)
79
+ end
80
+
81
+ say "Proceeding to next lesson: #{content["title"]}"
82
+ client.download_and_extract(content)
83
+ client.complete(content["position"])
84
+ display_content(content, !options[:"no-open"])
85
+ end
86
+
87
+ desc "current", "Open the current lesson"
88
+ option :"no-open", type: :boolean
89
+ def current
90
+ exit_with_no_key
91
+ content = client.current
92
+ say "Opening: #{content["title"]}"
93
+ client.download_and_extract(content)
94
+ display_content(content, !options[:"no-open"])
95
+ end
96
+
97
+ desc "complete", "Mark the current lesson as complete"
98
+ def complete
99
+ say "Marked current lesson as complete"
100
+ client.complete(nil)
101
+ end
102
+
103
+ desc "list", "Show all available workshop lessons"
104
+ def list
105
+ ::CLI::UI::Frame.open("{{*}} {{bold:All Lessons}}", color: :green)
106
+
107
+ frame_open = false
108
+ client.list.each do |lesson|
109
+ if lesson["title"].start_with?("Section")
110
+ ::CLI::UI::Frame.close(nil) if frame_open
111
+ ::CLI::UI::Frame.open(lesson["title"])
112
+ frame_open = true
113
+ next
114
+ end
115
+
116
+ no_data = client.send(:client_data)["completed"].nil?
117
+ completed = client.send(:client_data)["completed"]&.include?(lesson["position"])
118
+
119
+ str = if no_data
120
+ ""
121
+ elsif completed
122
+ "\u{2705} "
123
+ else
124
+ "\u{274C} "
125
+ end
126
+
127
+ case lesson["style"]
128
+ when "video"
129
+ puts str + ::CLI::UI.fmt("{{red:#{lesson["title"]}}}")
130
+ when "quiz"
131
+ # puts ::CLI::UI.fmt "{{green:#{" " + lesson["title"]}}}"
132
+ when "lab"
133
+ puts str + ::CLI::UI.fmt("{{yellow:#{" " + lesson["title"]}}}")
134
+ when "text"
135
+ puts str + ::CLI::UI.fmt("{{magenta:#{" " + lesson["title"]}}}")
136
+ else
137
+ puts str + ::CLI::UI.fmt("{{magenta:#{" " + lesson["title"]}}}")
138
+ end
139
+ end
140
+
141
+ ::CLI::UI::Frame.close(nil)
142
+ ::CLI::UI::Frame.close(nil, color: :green)
143
+ end
144
+
145
+ desc "download", "Download all workshop contents"
146
+ def download
147
+ exit_with_no_key
148
+ total = client.list.size
149
+ client.list.each do |content|
150
+ current = client.list.index(content) + 1
151
+ puts "Downloading #{content["title"]} (#{current}/#{total})"
152
+ client.download_and_extract(content)
153
+ end
154
+ end
155
+
156
+ desc "show", "Show any individal workshop lesson"
157
+ option :"no-open", type: :boolean
158
+ option :quizzes, type: :boolean
159
+ def show
160
+ exit_with_no_key
161
+ title = ::CLI::UI::Prompt.ask(
162
+ "Which lesson would you like to view?",
163
+ options: client.list.reject { |l| !options[:quizzes] && l["title"] == "Quiz" }.map { |l| " " * l["indent"] + l["title"] }
164
+ )
165
+ title.strip!
166
+ content_order = client.list.find { |l| l["title"] == title }["position"]
167
+ content = client.show(content_order)
168
+ client.download_and_extract(content)
169
+ display_content(content, !options[:"no-open"])
170
+ end
171
+
172
+ desc "set_progress", "Set current lesson to a particular lesson"
173
+ def set_progress
174
+ title = ::CLI::UI::Prompt.ask(
175
+ "Which lesson would you like to set your progress to? All prior lessons will be marked complete",
176
+ options: client.list.reject { |l| l["title"] == "Quiz" }.map { |l| " " * l["indent"] + l["title"] }
177
+ )
178
+ title.strip!
179
+ content_order = client.list.find { |l| l["title"] == title }["position"]
180
+ content = client.set_progress(content_order, all_prior: true)
181
+ say "Setting current progress to #{content.last["title"]}"
182
+ end
183
+
184
+ desc "reset", "Erase all progress and start over"
185
+ def reset
186
+ return unless ::CLI::UI.confirm("Are you sure you want to erase all of your progress?", default: false)
187
+ say "Resetting progress."
188
+ client.set_progress(nil)
60
189
  end
61
190
 
62
191
  no_commands do
@@ -67,14 +196,67 @@ module RPW
67
196
 
68
197
  private
69
198
 
199
+ def exit_with_no_key
200
+ unless client.setup?
201
+ say "You have not yet set up the client. Run $ rpw start"
202
+ exit(1)
203
+ end
204
+ unless client.directories_ready?
205
+ say "You are not in your workshop scratch directory, or you have not yet"
206
+ say "set up the client. Change directory or run $ rpw start"
207
+ exit(1)
208
+ end
209
+ end
210
+
70
211
  def client
71
212
  @client ||= RPW::Client.new
72
213
  end
73
214
 
215
+ def display_content(content, open_after)
216
+ openable = false
217
+ case content["style"]
218
+ when "video"
219
+ location = "video/#{content["s3_key"]}"
220
+ openable = true
221
+ when "quiz"
222
+ Quiz.start(["give_quiz", "quiz/" + content["s3_key"]])
223
+ when "lab"
224
+ location = "lab/#{content["s3_key"][0..-8]}"
225
+ openable = true
226
+ when "text"
227
+ location = "text/#{content["s3_key"]}"
228
+ openable = true
229
+ when "cgrp"
230
+ say "The Complete Guide to Rails Performance has been downloaded and extracted to the ./cgrp directory."
231
+ say "All source code for the CGRP is in the src directory, PDF and other compiled formats are in the release directory."
232
+ say "You can check it out now, or to continue: $ rpw next "
233
+ end
234
+ if location
235
+ if openable && !open_after
236
+ say "Download complete. Open with: $ #{open_command} #{location}"
237
+ elsif open_after && openable
238
+ exec "#{open_command} #{location}"
239
+ end
240
+ end
241
+ end
242
+
243
+ require "rbconfig"
244
+ def open_command
245
+ host_os = RbConfig::CONFIG["host_os"]
246
+ case host_os
247
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
248
+ "start"
249
+ when /darwin|mac os/
250
+ "open"
251
+ else
252
+ "xdg-open"
253
+ end
254
+ end
255
+
74
256
  def warn_if_already_started
75
257
  return unless client.setup?
76
- exit(0) unless yes? "You have already started the workshop. Continuing "\
77
- "this command will wipe all of your current progress. Continue? (y/N)"
258
+ exit(0) unless ::CLI::UI.confirm "You have already started the workshop. Continuing "\
259
+ "this command will wipe all of your current progress. Continue?", default: false
78
260
  end
79
261
 
80
262
  def check_version
@@ -83,12 +265,5 @@ module RPW
83
265
  say "WARNING: Please run `$ gem install rpw`"
84
266
  end
85
267
  end
86
-
87
- def check_setup
88
- unless client.setup? || current_command_chain == [:start]
89
- say "WARNING: You do not have a purchase key set. Run `$ rpw start`"
90
- exit(0)
91
- end
92
- end
93
268
  end
94
269
  end
data/lib/rpw/cli/key.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  module RPW
2
2
  class Key < SubCommandBase
3
- class_before :exit_with_no_key
4
-
5
3
  desc "register [EMAIL_ADDRESS]", "Change email registered with Speedshop. One-time only."
6
4
  def register(email)
5
+ unless client.setup?
6
+ say "You have not yet set up the client. Run $ rpw start"
7
+ exit(1)
8
+ end
7
9
  if client.register_email(email)
8
10
  say "Key registered with #{email}. You should receive a Slack invite soon."
9
11
  else
data/lib/rpw/cli/quiz.rb CHANGED
@@ -14,8 +14,8 @@ module RPW
14
14
  def question(data)
15
15
  puts data["prompt"]
16
16
  data["answer_choices"].each { |ac| puts ac }
17
- provided_answer = ask("Your answer?")
18
- answer_digest = Digest::MD5.hexdigest(data["prompt"] + provided_answer.upcase)
17
+ provided_answer = ::CLI::UI::Prompt.ask("Your answer?", options: %w[A B C D])
18
+ answer_digest = Digest::MD5.hexdigest(data["prompt"] + provided_answer)
19
19
  if answer_digest == data["answer_digest"]
20
20
  say "Correct!"
21
21
  else
@@ -13,18 +13,6 @@ module RPW
13
13
  def client
14
14
  @client ||= RPW::Client.new
15
15
  end
16
-
17
- def exit_with_no_key
18
- unless client.setup?
19
- say "You have not yet set up the client. Run $ rpw start"
20
- exit(1)
21
- end
22
- unless client.directories_ready?
23
- say "You are not in your workshop scratch directory, or you have not yet"
24
- say "set up the client. Change directory or run $ rpw start"
25
- exit(1)
26
- end
27
- end
28
16
  end
29
17
  end
30
18
  end
data/lib/rpw/client.rb CHANGED
@@ -25,6 +25,11 @@ module RPW
25
25
  list.sort_by { |c| c["position"] }.find { |c| c["position"] > current_position }
26
26
  end
27
27
 
28
+ def current
29
+ return list.first unless client_data["completed"]
30
+ list.sort_by { |c| c["position"] }.find { |c| c["position"] == current_position }
31
+ end
32
+
28
33
  def list
29
34
  @list ||= begin
30
35
  if client_data["content_cache_generated"] &&
@@ -44,8 +49,7 @@ module RPW
44
49
  end
45
50
 
46
51
  def show(content_pos)
47
- content_pos = current_position if content_pos == :current
48
- gateway.get_content_by_position(content_pos)
52
+ list.find { |l| l["position"] == content_pos }
49
53
  end
50
54
 
51
55
  def directory_setup(home_dir_ok = true)
@@ -76,22 +80,17 @@ module RPW
76
80
  end
77
81
  end
78
82
 
79
- def progress
80
- completed_lessons = client_data["completed"] || []
81
- {
82
- completed: completed_lessons.size,
83
- total: list.size,
84
- current_lesson: list.find { |c| c["position"] == current_position },
85
- sections: chart_section_progress(list, completed_lessons)
86
- }
87
- end
88
-
89
- def set_progress(pos)
83
+ def set_progress(pos, all_prior: false)
90
84
  client_data["completed"] = [] && return if pos.nil?
91
- lesson = list.find { |l| l["position"] == pos }
92
- raise Error.new("No such lesson - use the IDs in $ rpw lesson list") unless lesson
93
- client_data["completed"] += [pos]
94
- lesson
85
+ if all_prior
86
+ lessons = list.select { |l| l["position"] <= pos }
87
+ client_data["completed"] = lessons.map { |l| l["position"] }
88
+ lessons
89
+ else
90
+ lesson = list.find { |l| l["position"] == pos }
91
+ client_data["completed"] += [pos]
92
+ lesson
93
+ end
95
94
  end
96
95
 
97
96
  def latest_version?
@@ -132,9 +131,13 @@ module RPW
132
131
  end
133
132
 
134
133
  def complete(position)
135
- reset_progress unless client_data["completed"]
136
- # we actually have to put the _next_ lesson on the completed stack
137
- set_progress(self.next["position"])
134
+ if client_data["completed"]
135
+ # we actually have to put the _next_ lesson on the completed stack
136
+ set_progress(self.next["position"])
137
+ else
138
+ client_data["completed"] = []
139
+ set_progress(position)
140
+ end
138
141
  end
139
142
 
140
143
  private
@@ -143,32 +146,13 @@ module RPW
143
146
  @current_position ||= client_data["completed"]&.last || 0
144
147
  end
145
148
 
146
- def chart_section_progress(contents, completed)
147
- contents.group_by { |c| c["position"] / 100 }
148
- .each_with_object([]) do |(_, c), memo|
149
- completed_str = c.map { |l|
150
- if l["position"] == current_position
151
- "O"
152
- elsif completed.include?(l["position"])
153
- "X"
154
- else
155
- "."
156
- end
157
- }.join
158
- memo << {
159
- title: c[0]["title"],
160
- progress: completed_str
161
- }
162
- end
163
- end
164
-
165
149
  def client_data
166
150
  @client_data ||= ClientData.new
167
151
  end
168
152
 
169
153
  def extract_content(content)
170
154
  folder = content["style"]
171
- `tar -C #{folder} -xvzf #{folder}/#{content["s3_key"]}`
155
+ `tar -C #{folder} -xzf #{folder}/#{content["s3_key"]}`
172
156
  end
173
157
  end
174
158
  end
@@ -20,8 +20,8 @@ module RPW
20
20
  begin
21
21
  File.open(filestore_location, "w") { |f| f.write(YAML.dump(data)) }
22
22
  rescue
23
- raise Error, "The RPW data at #{filestore_location} is not writable. \
24
- Check your file permissions."
23
+ # raise Error, "The RPW data at #{filestore_location} is not writable. \
24
+ # Check your file permissions."
25
25
  end
26
26
  end
27
27
 
data/lib/rpw/gateway.rb CHANGED
@@ -16,16 +16,6 @@ module RPW
16
16
  Excon.get(domain + "/license", user: key).status == 200
17
17
  end
18
18
 
19
- def get_content_by_position(position)
20
- response = Excon.get(domain + "/contents/positional?position=#{position}", user: @key)
21
- if response.status == 200
22
- JSON.parse(response.body)
23
- else
24
- puts response.inspect
25
- raise Error, "There was a problem fetching this content."
26
- end
27
- end
28
-
29
19
  def list_content
30
20
  response = Excon.get(domain + "/contents", user: @key)
31
21
  if response.status == 200
@@ -37,21 +27,21 @@ module RPW
37
27
  end
38
28
 
39
29
  def download_content(content, folder:)
40
- puts "Downloading #{content["title"]}..."
41
- downloaded_file = File.open("#{folder}/#{content["s3_key"]}.partial", "w")
42
- streamer = lambda do |chunk, remaining_bytes, total_bytes|
43
- downloaded_file.write(chunk)
44
- print 13.chr
45
- print "Remaining: #{(remaining_bytes.to_f / total_bytes * 100).round(2).to_s.rjust(8)}%" if remaining_bytes
46
- end
47
- response = Excon.get(content["url"], response_block: streamer)
48
- unless response.status == 200
49
- puts response.inspect
50
- raise Error.new("Server problem: #{response.status}")
30
+ ::CLI::UI::Progress.progress do |bar|
31
+ downloaded_file = File.open("#{folder}/#{content["s3_key"]}.partial", "w")
32
+ streamer = lambda do |chunk, remaining_bytes, total_bytes|
33
+ downloaded_file.write(chunk)
34
+ bar.tick(set_percent: 1 - (remaining_bytes.to_f / total_bytes).round(2))
35
+ end
36
+ response = Excon.get(content["url"], response_block: streamer)
37
+ unless response.status == 200
38
+ puts response.inspect
39
+ raise Error.new("Server problem: #{response.status}")
40
+ end
41
+ downloaded_file.close
42
+ File.rename(downloaded_file, "#{folder}/#{content["s3_key"]}")
43
+ bar.tick(set_percent: 1)
51
44
  end
52
- downloaded_file.close
53
- print "\n"
54
- File.rename(downloaded_file, "#{folder}/#{content["s3_key"]}")
55
45
  end
56
46
 
57
47
  def latest_version?
data/lib/rpw/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RPW
2
- VERSION = "1.0.0"
2
+ VERSION = "1.4.0"
3
3
  end
data/rpw.gemspec CHANGED
@@ -27,4 +27,5 @@ Gem::Specification.new do |spec|
27
27
  spec.add_dependency "thor"
28
28
  spec.add_dependency "thor-hollaback"
29
29
  spec.add_dependency "excon"
30
+ spec.add_dependency "cli-ui"
30
31
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rpw
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Berkopec
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-22 00:00:00.000000000 Z
11
+ date: 2021-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: cli-ui
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  description:
56
70
  email:
57
71
  - nate@speedshop.co
@@ -76,8 +90,6 @@ files:
76
90
  - lib/rpw/cli.rb
77
91
  - lib/rpw/cli/bannerlord.rb
78
92
  - lib/rpw/cli/key.rb
79
- - lib/rpw/cli/lesson.rb
80
- - lib/rpw/cli/progress.rb
81
93
  - lib/rpw/cli/quiz.rb
82
94
  - lib/rpw/cli/sub_command_base.rb
83
95
  - lib/rpw/client.rb
@@ -107,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
119
  - !ruby/object:Gem::Version
108
120
  version: '0'
109
121
  requirements: []
110
- rubygems_version: 3.1.4
122
+ rubygems_version: 3.2.3
111
123
  signing_key:
112
124
  specification_version: 4
113
125
  summary: A CLI for the Rails Performance Workshop.
@@ -1,101 +0,0 @@
1
- module RPW
2
- class Lesson < SubCommandBase
3
- class_before :exit_with_no_key
4
-
5
- desc "next", "Proceed to the next lesson of the workshop"
6
- option :"no-open"
7
- def next
8
- say "Proceeding to next lesson..."
9
- content = client.next
10
-
11
- if content.nil?
12
- RPW::CLI.new.print_banner
13
- say "Congratulations!"
14
- say "You have completed the Rails Performance Workshop."
15
- exit(0)
16
- end
17
-
18
- client.download_and_extract(content)
19
- client.complete(content["position"])
20
- display_content(content, !options[:"no-open"])
21
- end
22
-
23
- desc "complete", "Mark the current lesson as complete"
24
- def complete
25
- say "Marked current lesson as complete"
26
- client.complete(nil)
27
- end
28
-
29
- desc "list", "Show all available workshop lessons"
30
- def list
31
- say "All available workshop lessons:"
32
- say "Use [ID] for the show/download command"
33
- say "[ID]: Lesson Name"
34
- client.list.each do |lesson|
35
- puts "[#{lesson["position"]}]:#{" " * lesson["indent"]} #{lesson["title"]}"
36
- end
37
- end
38
-
39
- desc "download [CONTENT | all]", "Download one or all workshop contents"
40
- def download(content_pos)
41
- to_download = if content_pos.downcase == "all"
42
- client.list
43
- else
44
- [client.show(content_pos)]
45
- end
46
- to_download.each { |content| client.download_and_extract(content) }
47
- end
48
-
49
- desc "show [CONTENT]", "Show any workshop lesson, shows current lesson w/no arguments"
50
- option :"no-open"
51
- def show(content_order = :current)
52
- content = client.show(content_order)
53
- client.download_and_extract(content)
54
- display_content(content, !options[:"no-open"])
55
- end
56
-
57
- private
58
-
59
- def display_content(content, open_after)
60
- say "Current Lesson: #{content["title"]}"
61
- openable = false
62
- case content["style"]
63
- when "video"
64
- location = "video/#{content["s3_key"]}"
65
- openable = true
66
- when "quiz"
67
- Quiz.start(["give_quiz", "quiz/" + content["s3_key"]])
68
- when "lab"
69
- location = "lab/#{content["s3_key"][0..-8]}"
70
- openable = true
71
- when "text"
72
- location = "text/#{content["s3_key"]}"
73
- openable = true
74
- when "cgrp"
75
- say "The Complete Guide to Rails Performance has been downloaded and extracted to the ./cgrp directory."
76
- say "All source code for the CGRP is in the src directory, PDF and other compiled formats are in the release directory."
77
- say "You can check it out now, or to continue: $ rpw lesson next "
78
- end
79
- if location
80
- if openable && !open_after
81
- say "Download complete. Open with: $ #{open_command} #{location}"
82
- elsif open_after && openable
83
- exec "#{open_command} #{location}"
84
- end
85
- end
86
- end
87
-
88
- require "rbconfig"
89
- def open_command
90
- host_os = RbConfig::CONFIG["host_os"]
91
- case host_os
92
- when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
93
- "start"
94
- when /darwin|mac os/
95
- "open"
96
- else
97
- "xdg-open"
98
- end
99
- end
100
- end
101
- end
@@ -1,34 +0,0 @@
1
- module RPW
2
- class Progress < SubCommandBase
3
- class_before :exit_with_no_key
4
-
5
- desc "set [LESSON]", "Set current lesson to a particular lesson"
6
- def set(pos)
7
- lesson = client.set_progress(pos.to_i)
8
- say "Set current progress to #{lesson["title"]}"
9
- end
10
-
11
- desc "reset", "Erase all progress and start over"
12
- def reset
13
- return unless yes? "Are you sure you want to reset your progress? (Y/N)"
14
- say "Resetting progress."
15
- client.set_progress(nil)
16
- end
17
-
18
- desc "show", "Show current workshop progress"
19
- def show
20
- data = client.progress
21
- say "The Rails Performance Workshop"
22
- say "You have completed #{data[:completed]} out of #{data[:total]} total sections."
23
- say "Current lesson: #{data[:current_lesson]["title"]}" if data[:current_lesson]
24
- say "Progress by Section (X == completed, O == current):"
25
- data[:sections].each do |section|
26
- say "#{section[:title]}: #{section[:progress]}"
27
- end
28
- end
29
-
30
- private
31
-
32
- default_task :show
33
- end
34
- end