slack_progress_bar 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e51319bfad1449d02de2bb1bf0cf42667d6ef61493a08e71bc3091cc893482cc
4
+ data.tar.gz: e9abfed3a6fdcb1004226926732f51cfd857d0dcb98211cf7076021b490b701b
5
+ SHA512:
6
+ metadata.gz: 408b6e327c38a008aceb3a28024e76ca9da1c87eb80fbda7e2b3832e23b38d340c32a8e653e29b32d01a2d61962d994577103a5fd009c34a4dba2d21d4ceb47c
7
+ data.tar.gz: 28611fa55290eed2374251d8f71e1db9c6388dc3a790406fe9571337ea6b10d6c78121e3c222047da29d8bfc538f0393ce3ac6dff7a6fec94c8a795eee4bfc9a
@@ -0,0 +1 @@
1
+ github: [laserlemon]
@@ -0,0 +1,31 @@
1
+ name: Rake
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ pull_request:
7
+ branches:
8
+ - main
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby-version:
15
+ - "2.6"
16
+ - "2.7"
17
+ - "3.0"
18
+ - head
19
+ steps:
20
+ - uses: actions/checkout@v2
21
+ - name: Set up Ruby
22
+ uses: ruby/setup-ruby@v1
23
+ with:
24
+ ruby-version: ${{ matrix.ruby-version }}
25
+ bundler-cache: true
26
+ - name: Install ImageMagick
27
+ run: |
28
+ sudo apt-get install imagemagick
29
+ convert -version
30
+ - name: Run tests
31
+ run: bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,47 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 2.6
4
+
5
+ Layout/ArgumentAlignment:
6
+ Enabled: true
7
+ EnforcedStyle: with_fixed_indentation
8
+
9
+ Layout/LineLength:
10
+ Enabled: false
11
+
12
+ Layout/MultilineMethodCallIndentation:
13
+ Enabled: true
14
+ EnforcedStyle: indented
15
+
16
+ Metrics:
17
+ Enabled: false
18
+
19
+ Naming/RescuedExceptionsVariableName:
20
+ Enabled: true
21
+ PreferredName: error
22
+
23
+ Style/BlockDelimiters:
24
+ Enabled: true
25
+ Exclude:
26
+ - "spec/**/*_spec.rb"
27
+
28
+ Style/Documentation:
29
+ Enabled: false
30
+
31
+ Style/IfUnlessModifier:
32
+ Enabled: false
33
+
34
+ Style/StringLiterals:
35
+ Enabled: true
36
+ EnforcedStyle: double_quotes
37
+
38
+ Style/SymbolArray:
39
+ Enabled: false
40
+
41
+ Style/TrailingCommaInArguments:
42
+ Enabled: true
43
+ EnforcedStyleForMultiline: comma
44
+
45
+ Style/TrailingCommaInHashLiteral:
46
+ Enabled: true
47
+ EnforcedStyleForMultiline: comma
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at steve.richert@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "rqrcode", "~> 2.0"
8
+ gem "rspec", "~> 3.10"
9
+ gem "rubocop", "~> 1.17.0"
10
+ gem "rubocop-rake", "~> 0.5.1"
11
+ gem "rubocop-rspec", "~> 2.4.0"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Steve Richert
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,246 @@
1
+ # Slack Progress Bar<br><img src="https://user-images.githubusercontent.com/34264/125970661-b3229a24-439c-43e5-9108-97f46a2c47a6.png" alt="Progress bar preview" width="404" height="20" aria-hidden="true">
2
+
3
+ Create _beautiful_ progress bars for Slack using **emoji**!
4
+
5
+ Slack Progress Bar is a Ruby library capable of [generating a progress bar](https://github.com/laserlemon/slack_progress_bar/blob/main/README.md#generating-a-progress-bar) made with Slack emoji. The library can also [generate the emoji](https://github.com/laserlemon/slack_progress_bar/blob/main/README.md#generating-the-emoji) images that you'll need in your Slack workspace.
6
+
7
+ [Try it out!](https://laserlemon.github.io/slack_progress_bar)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem "slack_progress_bar"
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ ```
20
+ $ bundle install
21
+ ```
22
+
23
+ Or install it yourself as:
24
+
25
+ ```
26
+ $ gem install slack_progress_bar
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Configuration
32
+
33
+ The library uses default colors based on the [GitHub Primer color system](https://primer.style/css/support/color-system) that allow you to hit the ground running without any in-app configuration.
34
+
35
+ | Alias | Letter | Hex value | Preview |
36
+ |:--- |:---:|:--- |:---:|
37
+ | `purple` | `p` | `6f42c1ff` | <img src="https://user-images.githubusercontent.com/34264/125973904-b82923e7-9457-4d47-9e38-70c36ab3f07f.png" width="20" height="20"> |
38
+ | `blue` | `b` | `0366d6ff` | <img src="https://user-images.githubusercontent.com/34264/125973900-f3faa84f-d7de-4102-b39e-6e5d12a443ed.png" width="20" height="20"> |
39
+ | `green` | `g` | `28a745ff` | <img src="https://user-images.githubusercontent.com/34264/125973902-74679045-0d94-4b41-81e4-56275207d97d.png" width="20" height="20"> |
40
+ | `yellow` | `y` | `ffd33dff` | <img src="https://user-images.githubusercontent.com/34264/125973907-2d615a1f-1016-40c6-9061-3f6243527869.png" width="20" height="20"> |
41
+ | `orange` | `o` | `f66a0aff` | <img src="https://user-images.githubusercontent.com/34264/125973903-25daa839-16b1-43e8-91a3-7f4a134aa341.png" width="20" height="20"> |
42
+ | `red` | `r` | `d73a49ff` | <img src="https://user-images.githubusercontent.com/34264/125973905-2708f63c-1783-4daf-af23-da115e2612c9.png" width="20" height="20"> |
43
+ | `white` | `w` | `959da544` | <img src="https://user-images.githubusercontent.com/34264/125973906-c2a9dcf2-f009-4aa7-b303-529b7a24aa0f.png" width="20" height="20"> |
44
+
45
+ You can configure color naming and sequence in a Rails initializer or any other Ruby configuration file:
46
+
47
+ ```ruby
48
+ SlackProgressBar.configure do |config|
49
+ # These one-character color names are used in emoji image filenames.
50
+ # The last color configured here is the default and is used to fill the remainder of a progress bar.
51
+ config.letters = ["r", "g", "b", "w"]
52
+
53
+ # Define your own aliases that map to each letter above, useful when generating a progress bar.
54
+ config.aliases = {
55
+ red: "r",
56
+ green: "g",
57
+ blue: "b",
58
+ white: "w",
59
+ deleted: "r",
60
+ created: "g",
61
+ updated: "b",
62
+ }
63
+ end
64
+ ```
65
+
66
+ The default emoji prefix is `pb`. For example, one Slack emoji using the configuration above could be `:pb-rgbw:`. The separator is `-` and isn't configurable. You can customize the prefix to prevent naming collisions.
67
+
68
+ ```ruby
69
+ SlackProgressBar.configure do |config|
70
+ # Name emoji like `bar-gggg`.
71
+ config.prefix = "bar"
72
+ end
73
+ ```
74
+
75
+ ### Generating a progress bar
76
+
77
+ Slack Progress Bar's primary interface for generating a progress bar is the `SlackProgressBar.new` and `SlackProgressBar#.to_s` methods. Examples below will assume default configuration.
78
+
79
+ By default, Slack Progress Bar generates an empty progress bar with 14 emoji and rounded caps:
80
+
81
+ ```ruby
82
+ bar = SlackProgressBar.new
83
+ puts bar.to_s
84
+ ```
85
+
86
+ ```
87
+ :pb-w-a::pb-wwww::pb-wwww::pb-wwww::pb-wwww::pb-wwww::pb-wwww::pb-wwww::pb-wwww::pb-wwww::pb-wwww::pb-wwww::pb-wwww::pb-w-z:
88
+ ```
89
+
90
+ <img src="https://user-images.githubusercontent.com/34264/125977880-a36c14e0-3d7c-4891-a94f-0b024e3433a1.png" width="404" height="20">
91
+
92
+ But most folks want to show some, well… progress!
93
+
94
+ ```ruby
95
+ bar = SlackProgressBar.new(counts: { blue: 3, green: 2, red: 1 })
96
+ puts bar.to_s
97
+ ```
98
+
99
+ ```
100
+ :pb-b-a::pb-bbbb::pb-bbbb::pb-bbbb::pb-bbbb::pb-bbbb::pb-bbbb::pb-gggg::pb-gggg::pb-gggg::pb-gggg::pb-rrrr::pb-rrrr::pb-r-z:
101
+ ```
102
+
103
+ <img src="https://user-images.githubusercontent.com/34264/125979277-f59e926c-2fc2-4192-9bc1-6e36b28b6f13.png" width="404" height="20">
104
+
105
+ And if you want to indicate that progress is not yet finished:
106
+
107
+ ```ruby
108
+ # Equivalent to: SlackProgressBar.new(counts: { blue: 3, green: 2, red: 1, white: 4 })
109
+ bar = SlackProgressBar.new(counts: { blue: 3, green: 2, red: 1 }, total: 10)
110
+ puts bar.to_s
111
+ ```
112
+
113
+ ```
114
+ :pb-b-a::pb-bbbb::pb-bbbb::pb-bbbb::pb-bbgg::pb-gggg::pb-gggg::pb-rrrr::pb-rwww::pb-wwww::pb-wwww::pb-wwww::pb-wwww::pb-w-z:
115
+ ```
116
+
117
+ <img src="https://user-images.githubusercontent.com/34264/125979830-ccc02bba-a85c-4509-957b-3dd234500448.png" width="404" height="20">
118
+
119
+ You can also change the size (in emoji) of the progress bar from its default of 14:
120
+
121
+ ```ruby
122
+ bar = SlackProgressBar.new(counts: { blue: 3, green: 2, red: 1 }, total: 10, size: 10)
123
+ puts bar.to_s
124
+ ```
125
+
126
+ ```
127
+ :pb-b-a::pb-bbbb::pb-bbbb::pb-bggg::pb-gggg::pb-rrrw::pb-wwww::pb-wwww::pb-wwww::pb-w-z:
128
+ ```
129
+
130
+ <img src="https://user-images.githubusercontent.com/34264/125980287-0093d19f-0ed8-4558-9765-8d2cde706138.png" width="276" height="20">
131
+
132
+ Finally, you can choose to generate your progress bar with square caps:
133
+
134
+ ```ruby
135
+ bar = SlackProgressBar.new(counts: { blue: 3, green: 2, red: 1 }, total: 10, size: 10, rounded: false)
136
+ puts bar.to_s
137
+ ```
138
+
139
+ ```
140
+ :pb-bbbb::pb-bbbb::pb-bbbb::pb-gggg::pb-gggg::pb-rrrr::pb-wwww::pb-wwww::pb-wwww::pb-wwww:
141
+ ```
142
+
143
+ <img src="https://user-images.githubusercontent.com/34264/125980701-f9d8a08c-d19a-424d-9198-f18ecbbd0c8d.png" width="320" height="20">
144
+
145
+ ### Generating the emoji
146
+
147
+ The observent reader will notice that `SlackProgressBar#to_s` (above) only generates a string. For Slack to render your beautiful progress bar, you'll need to generate and upload beautiful Slack emoji images! The `slack_progress_bar` gem comes with a `slack_progress_bar` command line interface and we'll use the `slack_progress_bar generate` command to create all the images you'll need.
148
+
149
+ For the default configuration, run `slack_progress_bar generate` with no arguments. Images will be created in your current working directory.
150
+
151
+ ```bash
152
+ slack_progress_bar generate
153
+ ```
154
+
155
+ The default configuration generates 233 images, which need to be uploaded to your Slack workspace. As of this writing, there is no API available to automate this manual process! 😅 You can reduce the number of images required by reducing the number of colors in your configuration:
156
+
157
+ ```bash
158
+ slack_progress_bar generate --colors r:d73a49ff g:28a745ff b:0366d6ff w:959da544
159
+ ```
160
+
161
+ The command above, configured for four colors, will generate only 49 images. Use the table below to help determine how many colors you're willing to support:
162
+
163
+ | Colors | Images |
164
+ | ---:| ---:|
165
+ | 2 | 12 |
166
+ | 3 | 25 |
167
+ | 4 | 48 |
168
+ | 5 | 86 |
169
+ | 6 | 145 |
170
+ | 7 | 232 |
171
+
172
+ When generating your images, each color you configure contains eight hex digits. The first six are RGB and the last two are alpha transparency. You can experiment with different colors and progress bar styles below:
173
+
174
+ ### 🛠️ [Slack Progress Bar Sandbox](https://laserlemon.github.io/slack_progress_bar)
175
+
176
+ ## Questions
177
+
178
+ ### How does it work?
179
+
180
+ Slack Progress Bar works in two steps. First it performs the one-time process of generating 128px square emoji images. Once those are uploaded to your Slack workspace, it references the names of those emoji to generate beautiful progress bars.
181
+
182
+ ### What's the progress bar's granularity?
183
+
184
+ By default, exactly 2%. That's why the default rounded progress bar size is 14 emoji. Each cap represents a "stripe" and every interior emoji represents four more stripes, for a total of 50 stripes at 2% per stripe. Below is an exploded progress bar to illustrate how the images are combined:
185
+
186
+ ```
187
+ :pb-p-a::pb-bbgg::pb-gyyy::pb-yooo::pb-oooo::pb-orrr::pb-rrrr::pb-rrrr::pb-rwww::pb-wwww::pb-wwww::pb-wwww::pb-wwww::pb-w-z:
188
+ ```
189
+
190
+ <img src="https://user-images.githubusercontent.com/34264/125985441-823e4c2f-379e-49f6-860a-e3d104bd173a.png" width="32" height="32">&nbsp;
191
+ <img src="https://user-images.githubusercontent.com/34264/125985470-67828620-291f-412d-b573-308271dbee5b.png" width="32" height="32">&nbsp;
192
+ <img src="https://user-images.githubusercontent.com/34264/125985517-28bb320f-0a58-4477-89f8-4e40a80eee6f.png" width="32" height="32">&nbsp;
193
+ <img src="https://user-images.githubusercontent.com/34264/125985564-b383037d-a0e8-4612-86c8-a1d9c8699e3c.png" width="32" height="32">&nbsp;
194
+ <img src="https://user-images.githubusercontent.com/34264/125985587-67b4b120-2780-4b5f-be2b-8477c966b71a.png" width="32" height="32">&nbsp;
195
+ <img src="https://user-images.githubusercontent.com/34264/125985616-8a121079-1c3e-4870-9df0-396b11cfbb06.png" width="32" height="32">&nbsp;
196
+ <img src="https://user-images.githubusercontent.com/34264/125985657-7f778bf3-2483-402c-97ee-72bb226c20de.png" width="32" height="32">&nbsp;
197
+ <img src="https://user-images.githubusercontent.com/34264/125985657-7f778bf3-2483-402c-97ee-72bb226c20de.png" width="32" height="32">&nbsp;
198
+ <img src="https://user-images.githubusercontent.com/34264/125985681-00ebfc9b-1caf-409f-966e-d2471fbe78ff.png" width="32" height="32">&nbsp;
199
+ <img src="https://user-images.githubusercontent.com/34264/125985711-afbcff71-f933-4ad3-bc37-e73f5906403b.png" width="32" height="32">&nbsp;
200
+ <img src="https://user-images.githubusercontent.com/34264/125985711-afbcff71-f933-4ad3-bc37-e73f5906403b.png" width="32" height="32">&nbsp;
201
+ <img src="https://user-images.githubusercontent.com/34264/125985711-afbcff71-f933-4ad3-bc37-e73f5906403b.png" width="32" height="32">&nbsp;
202
+ <img src="https://user-images.githubusercontent.com/34264/125985711-afbcff71-f933-4ad3-bc37-e73f5906403b.png" width="32" height="32">&nbsp;
203
+ <img src="https://user-images.githubusercontent.com/34264/125985743-150c8a04-8a90-4cd0-9da3-8614ef17410f.png" width="32" height="32">&nbsp;
204
+
205
+ ### Why not use narrow emoji?
206
+
207
+ When you use a narrow rectangular emoji in Slack, horizontal margins are added so that it takes up the same amount of space as a square emoji. The result is that you can't position multiple narrow emoji _right_ next to each other. That's why Slack Progress Bar uses perfectly square emoji throughout.
208
+
209
+ ### Can I animate my progress bar?
210
+
211
+ Sure! Slack's API gives you the ability to [create](https://api.slack.com/messaging/sending) and [update](https://api.slack.com/messaging/modifying) messages. So after you create your initial message with a blank progress bar, you can update that message as frequently as you like with the latest and greatest version of your progress bar.
212
+
213
+ <img src="https://user-images.githubusercontent.com/34264/125991787-d8c49424-106f-40ea-bf58-985adced4b1b.gif" width="374" height="51">
214
+
215
+ ### Can I change the color order?
216
+
217
+ Sadly, no. 😢 In an effort to reduce the number of images you need to generate and upload, the order in which colors appear is fixed. For Slack Progress Bar to generate 7-color, _order-flexible_ progress bars, it would need to generate 2,423 images rather than 232! 😱
218
+
219
+ ### Does it have dark mode?
220
+
221
+ Sure, kind of! Setting alpha transparency for your color configuration gives you some control over how your progress bars look in light and dark surroundings. For example, the default "white" color is dimmed using alpha transparency. The effect is a progress bar that's beautiful any time of day _or_ night:
222
+
223
+ <img src="https://user-images.githubusercontent.com/34264/125987921-3d7ac606-1ad3-4ebc-bf45-123df9b16411.png" width="448" height="32"><br>
224
+ <img src="https://user-images.githubusercontent.com/34264/125987920-491636ad-55a7-4c63-a243-feff09c284e6.png" width="448" height="32">
225
+
226
+ ### I have a different question!
227
+
228
+ Great! Please feel free to open an [issue](https://github.com/laserlemon/slack_progress_bar/issues/new) and I'll do my best to give you an answer. 💛
229
+
230
+ ## Development
231
+
232
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
233
+
234
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
235
+
236
+ ## Contributing
237
+
238
+ Bug reports and pull requests are welcome on GitHub at https://github.com/laserlemon/slack_progress_bar. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
239
+
240
+ ## License
241
+
242
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
243
+
244
+ ## Code of Conduct
245
+
246
+ Everyone interacting in the SlackProgressBar project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/laserlemon/slack_progress_bar/blob/main/CODE_OF_CONDUCT.md).
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require "bundler/setup"
6
+ require "slack_progress_bar/cli"
7
+
8
+ SlackProgressBar::CLI.start(ARGV)
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "slack_progress_bar/config"
4
+ require "slack_progress_bar/version"
5
+
6
+ class SlackProgressBar
7
+ def self.config
8
+ @config ||= Config.new
9
+ end
10
+
11
+ def self.configure
12
+ yield config
13
+ config.validate!
14
+ end
15
+
16
+ attr_reader :counts, :total, :size
17
+
18
+ # Create a new progress bar.
19
+ #
20
+ # The "counts" argument is a hash of string or symbol keys and integer values.
21
+ # Only keys present in either config.letters or config.aliases will be
22
+ # rendered in the resulting progress bar. If not provided, it's assumed that
23
+ # no progress has been made.
24
+ #
25
+ # The "total" argument is optional, defaulting to the sum of all provided
26
+ # counts. If a total is provided that is greater than the sum of the counts,
27
+ # the last configured color will be used to fill in the remainder of the bar.
28
+ # If a total is provided that is less than the sum of the counts provided, the
29
+ # greater value will be used to render the progress bar.
30
+ #
31
+ # The "size" argument specifies how many emoji will be used to render the
32
+ # progress bar. The default is 14 because the default progress bar has rounded
33
+ # ends that each represent only one stripe. The remaining 12 emoji each
34
+ # represent four stripes, for a total of 50 stripes, making each stripe count
35
+ # for 2% of the total. Any size may be given but each stripe may represent an
36
+ # unusual, floating point percentage of the total.
37
+ #
38
+ # The "rounded" argument accepts a boolean for whether the progress bar should
39
+ # have rounded ends. This is a matter of personal preference and will affect
40
+ # the whitespace on either side of the progress bar since the rounded end
41
+ # emoji images are mostly negative space.
42
+ def initialize(counts: {}, total: counts.values.sum, size: 14, rounded: true)
43
+ @counts = {}
44
+ @total = total
45
+ @size = size
46
+ @rounded = rounded
47
+
48
+ counts.each do |key, count|
49
+ letter =
50
+ case key
51
+ when *config.letters then key
52
+ when *config.aliases.keys then config.aliases.fetch(key)
53
+ end
54
+ @counts[letter] ||= 0
55
+ @counts[letter] += count
56
+ end
57
+ end
58
+
59
+ def to_s
60
+ emoji
61
+ end
62
+
63
+ private
64
+
65
+ def config
66
+ self.class.config
67
+ end
68
+
69
+ def rounded?
70
+ @rounded
71
+ end
72
+
73
+ def emoji
74
+ return @emoji if @emoji
75
+
76
+ emoji = +""
77
+
78
+ if rounded?
79
+ emoji << emojify(letters.slice(0), suffix: "a")
80
+ emoji << emojify(letters.slice(1, stripes - 2))
81
+ emoji << emojify(letters.slice(-1), suffix: "z")
82
+ else
83
+ emoji << emojify(letters)
84
+ end
85
+
86
+ @emoji = emoji
87
+ end
88
+
89
+ def emojify(letters, suffix: nil)
90
+ emoji = +""
91
+
92
+ letters.scan(/[a-z]{1,4}/) do |group|
93
+ emoji << group
94
+ .prepend(config.prefix, config.separator)
95
+ .tap { |s| s.concat(config.separator, suffix) if suffix }
96
+ .prepend(":")
97
+ .concat(":")
98
+ end
99
+
100
+ emoji
101
+ end
102
+
103
+ def letters
104
+ return @letters if @letters
105
+
106
+ letters = +""
107
+ running_total = 0
108
+
109
+ config.letters.each do |letter|
110
+ count = counts[letter]
111
+ next unless count
112
+
113
+ running_total += count
114
+ running_stripes = (running_total.to_f / effective_total * stripes).floor
115
+ letters = letters.ljust(running_stripes, letter)
116
+ end
117
+
118
+ @letters = letters.ljust(stripes, config.default_letter)
119
+ end
120
+
121
+ def effective_total
122
+ @effective_total ||= [counts.values.sum, total, 1].max
123
+ end
124
+
125
+ def stripes
126
+ @stripes ||= rounded? ? ((size - 2) * 4) + 2 : size * 4
127
+ end
128
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "slack_progress_bar/generator"
4
+
5
+ require "thor"
6
+
7
+ class SlackProgressBar
8
+ class CLI < Thor
9
+ defaults = Generator.new
10
+
11
+ desc "generate",
12
+ "Generate Slack emoji images"
13
+ option :colors,
14
+ desc: "A collection of key/value pairs where the keys are letters used for image file naming and the values are hex colors used for image generation",
15
+ type: :hash,
16
+ default: defaults.colors
17
+ option :prefix,
18
+ desc: "A string used as the prefix for every generated image file name",
19
+ type: :string,
20
+ default: defaults.prefix
21
+ option :output,
22
+ desc: "The directory where all generated image files will be saved",
23
+ type: :string,
24
+ default: defaults.output
25
+
26
+ def generate
27
+ generator = Generator.new(
28
+ colors: options[:colors],
29
+ prefix: options[:prefix],
30
+ output: options[:output],
31
+ )
32
+
33
+ generator.generate
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SlackProgressBar
4
+ class Config
5
+ InvalidConfigError = Class.new(StandardError)
6
+
7
+ DEFAULT_PREFIX = "pb"
8
+
9
+ # The default letters stand for: purple, blue, green, yellow, orange, red,
10
+ # and white. See SlackProgressBar::Generator::DEFAULT_COLORS for more
11
+ # information.
12
+ DEFAULT_LETTERS = %w[p b g y o r w].freeze
13
+
14
+ DEFAULT_ALIASES = {
15
+ purple: "p",
16
+ blue: "b",
17
+ green: "g",
18
+ yellow: "y",
19
+ orange: "o",
20
+ red: "r",
21
+ white: "w",
22
+ }.freeze
23
+
24
+ PREFIX_PATTERN = /\A[0-9a-z]+\z/.freeze
25
+ LETTER_PATTERN = /\A[a-z]\z/.freeze
26
+
27
+ SEPARATOR = "-"
28
+ LEFT_CAP_SUFFIX = "a"
29
+ RIGHT_CAP_SUFFIX = "z"
30
+ CIRCLE_SUFFIX = "o"
31
+
32
+ attr_accessor :prefix, :letters, :aliases
33
+
34
+ def initialize
35
+ @prefix = DEFAULT_PREFIX
36
+ @letters = DEFAULT_LETTERS
37
+ @aliases = DEFAULT_ALIASES
38
+ end
39
+
40
+ def validate!
41
+ unless prefix.is_a?(String)
42
+ raise InvalidConfigError, "The prefix must be a String. Found #{prefix.class.name}: #{prefix.inspect}"
43
+ end
44
+
45
+ unless PREFIX_PATTERN.match?(prefix)
46
+ raise InvalidConfigError, "The prefix must only contain digits 0-9 and/or lowercase letters a-z. Found: #{prefix.inspect}"
47
+ end
48
+
49
+ unless letters.is_a?(Array)
50
+ raise InvalidConfigError, "Configured letters must be an Array. Found #{letters.class.name}: #{letters.inspect}"
51
+ end
52
+
53
+ case letters.size
54
+ when 0
55
+ raise InvalidConfigError, "Provide at least two letters."
56
+ when 1
57
+ raise InvalidConfigError, "Provide at least two letters. The last letter will be used as the default."
58
+ end
59
+
60
+ unless letters.uniq.size == letters.size
61
+ raise InvalidConfigError, "Configured letters must be unique."
62
+ end
63
+
64
+ letters.each do |letter|
65
+ unless letter.is_a?(String)
66
+ raise InvalidConfigError, "Every letter must be a String. Found #{letter.class.name}: #{letter.inspect}"
67
+ end
68
+
69
+ unless LETTER_PATTERN.match?(letter)
70
+ raise InvalidConfigError, "The only valid letters are lowercase a-z. Found: #{letter.inspect}"
71
+ end
72
+ end
73
+
74
+ unless aliases.is_a?(Hash)
75
+ raise InvalidConfigError, "Configured aliases must be a Hash. Found #{aliases.class.name}: #{aliases.inspect}"
76
+ end
77
+
78
+ aliases.each do |name, letter|
79
+ unless name.is_a?(Symbol)
80
+ raise InvalidConfigError, "Every alias must have a Symbol name. Found #{name.class.name}: #{name.inspect}"
81
+ end
82
+
83
+ unless letter.is_a?(String)
84
+ raise InvalidConfigError, "Every alias must point to a String letter. Found #{letter.class.name}: #{letter.inspect}"
85
+ end
86
+
87
+ unless letters.include?(letter)
88
+ raise InvalidConfigError, "Every alias must point to a valid letter. Found: #{letter.inspect}"
89
+ end
90
+ end
91
+ end
92
+
93
+ def valid?
94
+ validate!
95
+ rescue InvalidConfigError
96
+ false
97
+ else
98
+ true
99
+ end
100
+
101
+ def default_letter
102
+ letters.last
103
+ end
104
+
105
+ def separator
106
+ SEPARATOR
107
+ end
108
+
109
+ def left_cap_suffix
110
+ LEFT_CAP_SUFFIX
111
+ end
112
+
113
+ def right_cap_suffix
114
+ RIGHT_CAP_SUFFIX
115
+ end
116
+
117
+ def circle_suffix
118
+ CIRCLE_SUFFIX
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "slack_progress_bar"
4
+
5
+ require "open3"
6
+ require "rqrcode"
7
+
8
+ class SlackProgressBar
9
+ class Generator
10
+ Error = Class.new(StandardError)
11
+ CommandFailedError = Class.new(Error)
12
+ ImageMagickError = Class.new(Error)
13
+ ImageMagickMissingError = Class.new(ImageMagickError)
14
+ ImageMagickOutdatedError = Class.new(ImageMagickError)
15
+ OutputError = Class.new(Error)
16
+ OutputMissingError = Class.new(OutputError)
17
+ OutputNotWritableError = Class.new(OutputError)
18
+
19
+ # The order in which these colors are configured is important; they will
20
+ # appear in the same order in a rendered progress bar.
21
+ #
22
+ # The default colors below were borrowed from GitHub's Primer color system.
23
+ # See: https://primer.style/css/support/color-system
24
+ #
25
+ # Each color consists of a six hex digits describing the RGB color and two
26
+ # additional hex digits to describe the color's transparency. "00" is fully
27
+ # transparent and "ff" is fully opaque.
28
+ DEFAULT_COLORS = {
29
+ "p" => "6f42c1ff", # purple
30
+ "b" => "0366d6ff", # blue
31
+ "g" => "28a745ff", # green
32
+ "y" => "ffd33dff", # yellow
33
+ "o" => "f66a0aff", # orange
34
+ "r" => "d73a49ff", # red
35
+ "w" => "959da544", # white
36
+ }.freeze
37
+
38
+ DEFAULT_OUTPUT = "./"
39
+
40
+ IMAGE_MAGICK_VERSION_PATTERN = /Version: ImageMagick (?<major_version>\d+)\.[^\s]+/.freeze
41
+ MINIMUM_IMAGE_MAGICK_MAJOR_VERSION = 6
42
+
43
+ attr_reader :colors, :prefix, :output
44
+
45
+ def initialize(colors: DEFAULT_COLORS, prefix: config.prefix, output: DEFAULT_OUTPUT)
46
+ @colors = colors
47
+ @prefix = prefix
48
+ @output = output
49
+ end
50
+
51
+ def config
52
+ @config ||= SlackProgressBar.config
53
+ end
54
+
55
+ def generate
56
+ check_image_magick!
57
+ check_output!
58
+
59
+ generate_left_caps
60
+ generate_right_caps
61
+ generate_circles
62
+ generate_stripes
63
+ generate_qr_code
64
+ end
65
+
66
+ private
67
+
68
+ def run_command(command)
69
+ output, status = Open3.capture2e(command)
70
+
71
+ raise CommandFailedError, output unless status.success?
72
+
73
+ output
74
+ end
75
+
76
+ def image_output_path(name)
77
+ File.join(output, "#{name}.png")
78
+ end
79
+
80
+ def check_image_magick!
81
+ output = run_command("convert -version")
82
+ major_version = output.slice(IMAGE_MAGICK_VERSION_PATTERN, :major_version).to_i
83
+
84
+ if major_version < MINIMUM_IMAGE_MAGICK_MAJOR_VERSION
85
+ raise ImageMagickOutdatedError, <<~ERR
86
+ ImageMagick is out of date.
87
+
88
+ Please upgrade to ImageMagick #{MINIMUM_IMAGE_MAGICK_MAJOR_VERSION} or later.
89
+ ERR
90
+ end
91
+
92
+ true
93
+ rescue CommandFailedError
94
+ raise ImageMagickMissingError, <<~ERR
95
+ ImageMagick's "convert" command was not found.
96
+
97
+ Please be sure that ImageMagick is installed, the "convert" command is
98
+ available, and your PATH is configured to find the "convert" command.
99
+ ERR
100
+ end
101
+
102
+ def check_output!
103
+ unless File.directory?(output)
104
+ raise OutputMissingError,
105
+ "Output directory #{output.inspect} was not found."
106
+ end
107
+
108
+ unless File.writable?(output)
109
+ raise OutputNotWritableError,
110
+ "Output directory #{output.inspect} is not writable."
111
+ end
112
+
113
+ true
114
+ end
115
+
116
+ def generate_left_caps
117
+ puts "Generating left caps"
118
+
119
+ colors.each do |letter, color|
120
+ name = [prefix, letter, config.left_cap_suffix].join(config.separator)
121
+ path = image_output_path(name)
122
+
123
+ run_command(%(convert -size 128x128 canvas:transparent -fill "##{color}" -draw "translate 127.5,63.5 circle 0,0 0,39.5" #{path}))
124
+ print "."
125
+ end
126
+
127
+ puts
128
+ end
129
+
130
+ def generate_right_caps
131
+ puts "Generating right caps"
132
+
133
+ colors.each do |letter, color|
134
+ name = [prefix, letter, config.right_cap_suffix].join(config.separator)
135
+ path = image_output_path(name)
136
+
137
+ run_command(%(convert -size 128x128 canvas:transparent -fill "##{color}" -draw "translate -0.5,63.5 circle 0,0 0,39.5" #{path}))
138
+ print "."
139
+ end
140
+
141
+ puts
142
+ end
143
+
144
+ def generate_circles
145
+ puts "Generating circles"
146
+
147
+ colors.each do |letter, color|
148
+ name = [prefix, letter, config.circle_suffix].join(config.separator)
149
+ path = image_output_path(name)
150
+
151
+ run_command(%(convert -size 128x128 canvas:transparent -fill "##{color}" -draw "translate 63.5,63.5 circle 0,0 0,39.5" #{path}))
152
+ print "."
153
+ end
154
+
155
+ puts
156
+ end
157
+
158
+ def generate_stripes
159
+ puts "Generating stripes"
160
+
161
+ colors.keys.repeated_combination(4).each do |four_letters|
162
+ four_colors = four_letters.map { |letter| colors.fetch(letter) }
163
+
164
+ command = +%(convert -size 128x128 canvas:transparent)
165
+
166
+ four_colors.each_with_index do |color, i|
167
+ x1 = i * 32
168
+ x2 = x1 + 31
169
+ command << %( -fill "##{color}" -draw "rectangle #{x1},24 #{x2},103")
170
+ end
171
+
172
+ name = four_letters.join.prepend(prefix, config.separator)
173
+ path = image_output_path(name)
174
+ command << %( #{path})
175
+
176
+ run_command(command)
177
+ print "."
178
+ end
179
+
180
+ puts
181
+ end
182
+
183
+ def generate_qr_code
184
+ puts "Generating QR code"
185
+
186
+ count = 1
187
+ previous_count = 0
188
+ configs = []
189
+
190
+ colors.each do |letter, color|
191
+ configs << "#{letter}:#{color}:#{count}"
192
+
193
+ count_to_add = previous_count
194
+ previous_count = count
195
+ count += count_to_add
196
+ end
197
+
198
+ anchor = configs.join("+")
199
+ url = "https://laserlemon.github.io/slack_progress_bar/##{anchor}"
200
+ qr = RQRCode::QRCode.new(url, level: :l)
201
+
202
+ name = "#{prefix}#{config.separator}qr"
203
+ path = image_output_path(name)
204
+
205
+ IO.binwrite(path, qr.as_png(
206
+ border_modules: 0,
207
+ resize_exactly_to: 128,
208
+ ))
209
+ puts "."
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SlackProgressBar
4
+ VERSION = Gem::Version.new("0.1.0")
5
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/slack_progress_bar/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "slack_progress_bar"
7
+ spec.version = SlackProgressBar::VERSION
8
+ spec.summary = "Generate beautiful progress bars using custom Slack emoji"
9
+
10
+ spec.author = "Steve Richert"
11
+ spec.email = "steve.richert@gmail.com"
12
+ spec.license = "MIT"
13
+
14
+ spec.files = Dir.chdir(__dir__) do
15
+ `git ls-files -z`.split("\x0").grep_v(/\A(bin|spec|Rakefile)/)
16
+ end
17
+
18
+ spec.add_runtime_dependency "thor", "~> 1.0"
19
+
20
+ spec.add_development_dependency "bundler", ">= 2"
21
+ spec.add_development_dependency "rake", ">= 12"
22
+
23
+ spec.required_ruby_version = ">= 2.6.0"
24
+
25
+ spec.bindir = "exe"
26
+ spec.executables = ["slack_progress_bar"]
27
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: slack_progress_bar
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Steve Richert
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-07-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '2'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '12'
55
+ description:
56
+ email: steve.richert@gmail.com
57
+ executables:
58
+ - slack_progress_bar
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".github/FUNDING.yml"
63
+ - ".github/workflows/rake.yml"
64
+ - ".gitignore"
65
+ - ".rspec"
66
+ - ".rubocop.yml"
67
+ - CODE_OF_CONDUCT.md
68
+ - Gemfile
69
+ - LICENSE.txt
70
+ - README.md
71
+ - exe/slack_progress_bar
72
+ - lib/slack_progress_bar.rb
73
+ - lib/slack_progress_bar/cli.rb
74
+ - lib/slack_progress_bar/config.rb
75
+ - lib/slack_progress_bar/generator.rb
76
+ - lib/slack_progress_bar/version.rb
77
+ - slack_progress_bar.gemspec
78
+ homepage:
79
+ licenses:
80
+ - MIT
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 2.6.0
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.2.19
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: Generate beautiful progress bars using custom Slack emoji
101
+ test_files: []