rpw 1.0.0 → 1.4.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.
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