rpw 0.0.6 → 1.3.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: 796740f7cd5cfebc0f46f72669da6d1b2941436c984d2a624a18200ae3466495
4
- data.tar.gz: 3b986dbab69c20f2e840b2197175119e428d19b117378ef9192a5ce31178cbac
3
+ metadata.gz: 4e52497c6e3760c9aa301c8eeae51459c46458625a809847f83a0d660d104754
4
+ data.tar.gz: 01aac30eece6c29a294baa6e6859ff3de5afe52cd33c5113527351430169d4b0
5
5
  SHA512:
6
- metadata.gz: 6b5530f032ece3e44819374cb7001530359a913e5b025e04f19313c6afeb1ec2450667783c8d44e20a18b1d0c16220a19f60d29275b1f3e9852a5401a2f2b7ba
7
- data.tar.gz: 1389deea5fe3705527d841abc84d9f6f33b112d5a8d02502bb8d8f8a5df90ffaedc520f8449da2891405d7702a31d65ac193087c12a5a3588afe53cf6f4dee14
6
+ metadata.gz: c86fdd2626b6d1e49a9c67c51e5476453799ae1a42013056f20064b59cf151e2779a28f5c6ebc7d0ee700f8cf3a1085f64370fa2282290e9eb80aa359433aa29
7
+ data.tar.gz: 74368f4f9e457c67ea43358c0e5fcb253bd38fa8487f5cd3fdd6b683a396b765ca0125d29366318536076fdd553020cc2d9349ca0548b15eeb772974de459bf5
@@ -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
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rpw (0.0.6)
4
+ rpw (1.3.0)
5
+ cli-ui
5
6
  excon
6
7
  thor
7
8
  thor-hollaback
@@ -10,6 +11,7 @@ GEM
10
11
  remote: https://rubygems.org/
11
12
  specs:
12
13
  ast (2.4.1)
14
+ cli-ui (1.4.0)
13
15
  excon (0.78.0)
14
16
  hollaback (0.1.0)
15
17
  minitest (5.14.2)
data/HISTORY.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## 1.3.0
2
+
3
+ * Added suggested schedules to README
4
+
5
+ ## 1.2.0
6
+
7
+ * Actual release version!
8
+ * Greatly streamlined commands
9
+
10
+ ## 1.1.0
11
+
12
+ * sick colors
13
+
14
+ ## 1.0.1
15
+
16
+ * Pre-launch bugfix
17
+
18
+ ## 1.0.0
19
+
20
+ * First release!
21
+
1
22
  ## 0.0.6
2
23
 
3
24
  * more bugfixes from beta testers
@@ -1,22 +1,94 @@
1
1
  ## Installation Requirements
2
2
 
3
- This client assumes you have `tar` installed.
3
+ This client assumes you're using Ruby 2.6 or later.
4
+
5
+ This client assumes you have `tar` installed and available on your PATH.
6
+
7
+ The way that the client opens files (using `open` or `xdg-open` depending on platform) assumes you have your default program for the following filetypes set correctly:
8
+
9
+ * .md for Markdown (XCode by default on Mac: you probably want to change that!)
10
+ * .mp4 for videos
11
+
12
+ ## Slack Invite
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
+
19
+ If you purchased the Workshop yourself, you will receive a Slack channel invitation
20
+ shortly. If you are attending the Workshop as part of a group and your license key
21
+ was provided to you, you need to register your key to get an invite:
22
+
23
+ ```
24
+ $ rpw key register [YOUR_EMAIL_ADDRESS]
25
+ ```
26
+
27
+ Please note you can only register your key once.
4
28
 
5
29
  ## Important Commands
6
30
 
7
31
  Here are some important commands for you to know:
8
32
 
9
- $ rpw lesson next | Proceed to the next part of the workshop.
10
- $ rpw lesson complete | Mark current lesson as complete.
11
- $ rpw lesson list | List all workshop lessons. Note each lesson is preceded with an ID.
12
- $ rpw lesson download | Download any or all lessons.
13
- $ rpw lesson show | Show any particular workshop lesson.
14
- $ rpw progress | Show where you're currently at in the workshop.
15
- $ rpw help | Help! You can also ask in Slack.
33
+ ```
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
+ ```
41
+
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
16
45
 
17
- Generally, you'll just be doing a lot of $ rpw lesson next
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`.
18
48
 
19
- ## Data Size Notice
49
+ ## Working Offline
50
+
51
+ By default, the course will download each piece of content as you progress through
52
+ the course. However, you can use `rpw download` to download all content
53
+ at once, and complete the workshop entirely offline.
20
54
 
21
55
  Videos in this workshop are generally about 100MB each, which means the entire
22
- course is about a 3 to 4GB download.
56
+ course is about a 3 to 4GB download.
57
+
58
+ ## Bugs and Support
59
+
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.
@@ -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,28 +32,159 @@ 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."
59
- say "Remember to ask on Slack for help if you get stuck or encounter bugs."
60
- say "Once 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
+ def show
159
+ exit_with_no_key
160
+ title = ::CLI::UI::Prompt.ask(
161
+ "Which lesson would you like to view?",
162
+ options: client.list.reject { |l| l["title"] == "Quiz" }.map { |l| " " * l["indent"] + l["title"] }
163
+ )
164
+ title.strip!
165
+ content_order = client.list.find { |l| l["title"] == title }["position"]
166
+ content = client.show(content_order)
167
+ client.download_and_extract(content)
168
+ display_content(content, !options[:"no-open"])
169
+ end
170
+
171
+ desc "set_progress", "Set current lesson to a particular lesson"
172
+ def set_progress
173
+ title = ::CLI::UI::Prompt.ask(
174
+ "Which lesson would you like to set your progress to? All prior lessons will be marked complete",
175
+ options: client.list.reject { |l| l["title"] == "Quiz" }.map { |l| " " * l["indent"] + l["title"] }
176
+ )
177
+ title.strip!
178
+ content_order = client.list.find { |l| l["title"] == title }["position"]
179
+ content = client.set_progress(content_order, all_prior: true)
180
+ say "Setting current progress to #{content.last["title"]}"
181
+ end
182
+
183
+ desc "reset", "Erase all progress and start over"
184
+ def reset
185
+ return unless ::CLI::UI.confirm("Are you sure you want to erase all of your progress?", default: false)
186
+ say "Resetting progress."
187
+ client.set_progress(nil)
61
188
  end
62
189
 
63
190
  no_commands do
@@ -68,28 +195,73 @@ module RPW
68
195
 
69
196
  private
70
197
 
198
+ def exit_with_no_key
199
+ unless client.setup?
200
+ say "You have not yet set up the client. Run $ rpw start"
201
+ exit(1)
202
+ end
203
+ unless client.directories_ready?
204
+ say "You are not in your workshop scratch directory, or you have not yet"
205
+ say "set up the client. Change directory or run $ rpw start"
206
+ exit(1)
207
+ end
208
+ end
209
+
71
210
  def client
72
211
  @client ||= RPW::Client.new
73
212
  end
74
213
 
214
+ def display_content(content, open_after)
215
+ openable = false
216
+ case content["style"]
217
+ when "video"
218
+ location = "video/#{content["s3_key"]}"
219
+ openable = true
220
+ when "quiz"
221
+ Quiz.start(["give_quiz", "quiz/" + content["s3_key"]])
222
+ when "lab"
223
+ location = "lab/#{content["s3_key"][0..-8]}"
224
+ openable = true
225
+ when "text"
226
+ location = "text/#{content["s3_key"]}"
227
+ openable = true
228
+ when "cgrp"
229
+ say "The Complete Guide to Rails Performance has been downloaded and extracted to the ./cgrp directory."
230
+ say "All source code for the CGRP is in the src directory, PDF and other compiled formats are in the release directory."
231
+ say "You can check it out now, or to continue: $ rpw next "
232
+ end
233
+ if location
234
+ if openable && !open_after
235
+ say "Download complete. Open with: $ #{open_command} #{location}"
236
+ elsif open_after && openable
237
+ exec "#{open_command} #{location}"
238
+ end
239
+ end
240
+ end
241
+
242
+ require "rbconfig"
243
+ def open_command
244
+ host_os = RbConfig::CONFIG["host_os"]
245
+ case host_os
246
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
247
+ "start"
248
+ when /darwin|mac os/
249
+ "open"
250
+ else
251
+ "xdg-open"
252
+ end
253
+ end
254
+
75
255
  def warn_if_already_started
76
256
  return unless client.setup?
77
- exit(0) unless yes? "You have already started the workshop. Continuing "\
78
- "this command will wipe all of your current progress. Continue? (y/N)"
257
+ exit(0) unless ::CLI::UI.confirm "You have already started the workshop. Continuing "\
258
+ "this command will wipe all of your current progress. Continue?", default: false
79
259
  end
80
260
 
81
261
  def check_version
82
262
  unless client.latest_version?
83
263
  say "WARNING: You are running an old version of rpw."
84
264
  say "WARNING: Please run `$ gem install rpw`"
85
- exit(0)
86
- end
87
- end
88
-
89
- def check_setup
90
- unless client.setup? || current_command_chain == [:start]
91
- say "WARNING: You do not have a purchase key set. Run `$ rpw start`"
92
- exit(0)
93
265
  end
94
266
  end
95
267
  end
@@ -1,14 +1,16 @@
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
10
12
  say "Key has already been registered. If you believe this is in error,"\
11
- " please email nate.berkopec@speedshop.co"
13
+ " please email support@speedshop.co"
12
14
  end
13
15
  end
14
16
  end
@@ -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
@@ -21,19 +21,35 @@ module RPW
21
21
  end
22
22
 
23
23
  def next
24
- contents = gateway.list_content
25
- return contents.first unless client_data["completed"]
26
- contents.delete_if { |c| client_data["completed"].include? c["position"] }
27
- contents.sort_by { |c| c["position"] }[1] # 0 would be the current lesson
24
+ return list.first unless client_data["completed"]
25
+ list.sort_by { |c| c["position"] }.find { |c| c["position"] > current_position }
26
+ end
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 }
28
31
  end
29
32
 
30
33
  def list
31
- gateway.list_content
34
+ @list ||= begin
35
+ if client_data["content_cache_generated"] &&
36
+ client_data["content_cache_generated"] >= Time.now - 60 * 60
37
+
38
+ client_data["content_cache"]
39
+ else
40
+ begin
41
+ client_data["content_cache"] = gateway.list_content
42
+ client_data["content_cache_generated"] = Time.now
43
+ client_data["content_cache"]
44
+ rescue
45
+ client_data["content_cache"] || (raise Error.new("No internet connection"))
46
+ end
47
+ end
48
+ end
32
49
  end
33
50
 
34
51
  def show(content_pos)
35
- content_pos = client_data["current_lesson"] if content_pos == :current
36
- gateway.get_content_by_position(content_pos)
52
+ list.find { |l| l["position"] == content_pos }
37
53
  end
38
54
 
39
55
  def directory_setup(home_dir_ok = true)
@@ -64,38 +80,23 @@ module RPW
64
80
  end
65
81
  end
66
82
 
67
- def increment_current_lesson!(position)
68
- mark_current_lesson_as_completed
69
- client_data["current_lesson"] = position
70
- end
71
-
72
- def progress
73
- contents = gateway.list_content
74
- completed_lessons = client_data["completed"] || []
75
- {
76
- completed: completed_lessons.size,
77
- total: contents.size,
78
- current_lesson: contents.find { |c| c["position"] == client_data["current_lesson"] },
79
- sections: chart_section_progress(contents, completed_lessons)
80
- }
81
- end
82
-
83
- def set_progress(lesson)
84
- client_data["current_lesson"] = lesson.to_i
85
- end
86
-
87
- def reset_progress
88
- client_data["current_lesson"] = 0
89
- client_data["completed"] = []
83
+ def set_progress(pos, all_prior: false)
84
+ client_data["completed"] = [] && return if pos.nil?
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
90
94
  end
91
95
 
92
96
  def latest_version?
93
97
  return true unless ClientData.exists?
94
-
95
- if client_data["last_version_check"]
96
- return true if client_data["last_version_check"] >= Time.now - (60 * 60 * 24)
97
- return false if client_data["last_version_check"] == false
98
- end
98
+ return true if client_data["last_version_check"] &&
99
+ client_data["last_version_check"] >= Time.now - (60 * 60)
99
100
 
100
101
  begin
101
102
  latest = gateway.latest_version?
@@ -129,31 +130,20 @@ module RPW
129
130
  end
130
131
  end
131
132
 
133
+ def complete(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
141
+ end
142
+
132
143
  private
133
144
 
134
- def mark_current_lesson_as_completed
135
- reset_progress unless client_data["current_lesson"] && client_data["completed"]
136
- client_data["completed"] ||= []
137
- client_data["completed"] += [client_data["current_lesson"] || 0]
138
- end
139
-
140
- def chart_section_progress(contents, completed)
141
- contents.group_by { |c| c["position"] / 100 }
142
- .each_with_object([]) do |(_, c), memo|
143
- completed_str = c.map { |l|
144
- if l["position"] == client_data["current_lesson"]
145
- "O"
146
- elsif completed.include?(l["position"])
147
- "X"
148
- else
149
- "."
150
- end
151
- }.join
152
- memo << {
153
- title: c[0]["title"],
154
- progress: completed_str
155
- }
156
- end
145
+ def current_position
146
+ @current_position ||= client_data["completed"]&.last || 0
157
147
  end
158
148
 
159
149
  def client_data
@@ -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
 
@@ -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,17 +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)}%"
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)
46
44
  end
47
- Excon.get(content["url"], response_block: streamer)
48
- downloaded_file.close
49
- print "\n"
50
- File.rename(downloaded_file, "#{folder}/#{content["s3_key"]}")
51
45
  end
52
46
 
53
47
  def latest_version?
@@ -1,3 +1,3 @@
1
1
  module RPW
2
- VERSION = "0.0.6"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -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: 0.0.6
4
+ version: 1.3.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-17 00:00:00.000000000 Z
11
+ date: 2020-11-23 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
@@ -1,99 +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 :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.increment_current_lesson!(content["position"])
20
- display_content(content, options[: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
27
- end
28
-
29
- desc "list", "Show all available workshop lessons"
30
- def list
31
- say "All available workshop lessons:"
32
- client.list.each do |lesson|
33
- puts "#{" " * lesson["indent"]}[#{lesson["position"]}]: #{lesson["title"]}"
34
- end
35
- end
36
-
37
- desc "download [CONTENT | all]", "Download one or all workshop contents"
38
- def download(content_pos)
39
- to_download = if content_pos.downcase == "all"
40
- client.list
41
- else
42
- [client.show(content_pos)]
43
- end
44
- to_download.each { |content| client.download_and_extract(content) }
45
- end
46
-
47
- desc "show [CONTENT]", "Show any workshop lesson, shows current lesson w/no arguments"
48
- option :open
49
- def show(content_order = :current)
50
- content = client.show(content_order)
51
- client.download_and_extract(content)
52
- display_content(content, options[:open])
53
- end
54
-
55
- private
56
-
57
- def display_content(content, open_after)
58
- say "Current Lesson: #{content["title"]}"
59
- openable = false
60
- case content["style"]
61
- when "video"
62
- location = "video/#{content["s3_key"]}"
63
- openable = true
64
- when "quiz"
65
- Quiz.start(["give_quiz", "quiz/" + content["s3_key"]])
66
- when "lab"
67
- location = "lab/#{content["s3_key"][0..-8]}"
68
- when "text"
69
- location = "lab/#{content["s3_key"]}"
70
- openable = true
71
- when "cgrp"
72
- say "The Complete Guide to Rails Performance has been downloaded and extracted to the ./cgrp directory."
73
- say "All source code for the CGRP is in the src directory, PDF and other compiled formats are in the release directory."
74
- end
75
- if location
76
- if openable && !open_after
77
- say "This file can be opened automatically if you use the --open flag next time."
78
- say "e.g. $ rpw lesson next --open"
79
- say "Download complete. Open with: $ #{open_command} #{location}"
80
- elsif open_after && openable
81
- exec "#{open_command} #{location}"
82
- end
83
- end
84
- end
85
-
86
- require "rbconfig"
87
- def open_command
88
- host_os = RbConfig::CONFIG["host_os"]
89
- case host_os
90
- when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
91
- "start"
92
- when /darwin|mac os/
93
- "open"
94
- else
95
- "xdg-open"
96
- end
97
- end
98
- end
99
- end
@@ -1,32 +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(lesson)
7
- client.set_progress(lesson)
8
- end
9
-
10
- desc "reset", "Erase all progress and start over"
11
- def reset
12
- yes? "Are you sure you want to reset your progress? (Y/N)"
13
- client.reset_progress
14
- end
15
-
16
- desc "show", "Show current workshop progress"
17
- def show
18
- data = client.progress
19
- say "The Rails Performance Workshop"
20
- say "You have completed #{data[:completed]} out of #{data[:total]} total sections."
21
- say "Current lesson: #{data[:current_lesson]["title"]}" if data[:current_lesson]
22
- say "Progress by Section (X == completed, O == current):"
23
- data[:sections].each do |section|
24
- say "#{section[:title]}: #{section[:progress]}"
25
- end
26
- end
27
-
28
- private
29
-
30
- default_task :show
31
- end
32
- end