rpw 0.0.6 → 1.3.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: 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