sail 3.6.0 → 3.6.1
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 +4 -4
- data/MIT-LICENSE +44 -0
- data/README.md +45 -28
- data/Rakefile +4 -4
- data/app/assets/images/sail/angle-left.svg +1 -1
- data/app/assets/images/sail/angle-right.svg +1 -1
- data/app/assets/images/sail/check.svg +1 -0
- data/app/assets/images/sail/cog.svg +1 -1
- data/app/assets/images/sail/external-link-alt.svg +1 -0
- data/app/assets/images/sail/sliders-h.svg +1 -0
- data/app/assets/images/sail/times.svg +1 -0
- data/app/assets/images/sail/undo.svg +1 -0
- data/app/controllers/sail/settings_controller.rb +9 -11
- data/app/models/sail/entry.rb +1 -1
- data/app/models/sail/setting.rb +8 -1
- data/app/views/sail/settings/_search.html.erb +2 -2
- data/app/views/sail/settings/_setting.html.erb +4 -4
- data/lib/generators/sail/views/views_generator.rb +16 -0
- data/lib/sail/engine.rb +5 -12
- data/lib/sail/instrumenter.rb +4 -8
- data/lib/sail/version.rb +1 -1
- metadata +11 -219
- data/app/assets/images/sail/checkmark.svg +0 -1
- data/app/assets/images/sail/error.svg +0 -1
- data/app/assets/images/sail/link.svg +0 -1
- data/app/assets/images/sail/reset.svg +0 -1
- data/app/assets/images/sail/sort.svg +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 489f0eae7521683d97bb3c27d7ffd09b8d3824ec80ae61bdfd0b016c11566df3
|
4
|
+
data.tar.gz: 2febbe6904763f83fecddbf310ae1f36c9205415090039ef1fbaa08a4d7e360c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c5803a6d070d20dc5dafb77451837d2d8342b52cdff3425ee723ce1124f1694dc8d4002e64e2a1b13d539331eb0c4973f2bd5989672f3158aab0e01bc91f687
|
7
|
+
data.tar.gz: c92b70dff126d83a57b84e6616e88d4f3f79d28c3aca0747c8352a4a93f516dc05c565044eaa4cb2187b055e036f03236c85a153e40c9013381e8c927fb49588
|
data/MIT-LICENSE
CHANGED
@@ -18,3 +18,47 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
The icons used by Sail's admin panel (all SVG files under app/assets/images/sail)
|
23
|
+
are all made by Font Awesome. The original license can be found below.
|
24
|
+
|
25
|
+
https://github.com/FortAwesome/Font-Awesome
|
26
|
+
|
27
|
+
## Font Awesome Free License
|
28
|
+
|
29
|
+
Font Awesome Free is free, open source, and GPL friendly. You can use it for
|
30
|
+
commercial projects, open source projects, or really almost whatever you want.
|
31
|
+
Full Font Awesome Free license: https://fontawesome.com/license/free.
|
32
|
+
|
33
|
+
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
|
34
|
+
|
35
|
+
In the Font Awesome Free download, the CC BY 4.0 license applies to all icons
|
36
|
+
packaged as SVG and JS file types.
|
37
|
+
|
38
|
+
# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL)
|
39
|
+
|
40
|
+
In the Font Awesome Free download, the SIL OFL license applies to all icons
|
41
|
+
packaged as web and desktop font files.
|
42
|
+
|
43
|
+
# Code: MIT License (https://opensource.org/licenses/MIT)
|
44
|
+
|
45
|
+
In the Font Awesome Free download, the MIT license applies to all non-font and
|
46
|
+
non-icon files.
|
47
|
+
|
48
|
+
# Attribution
|
49
|
+
|
50
|
+
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
|
51
|
+
Awesome Free files already contain embedded comments with sufficient
|
52
|
+
attribution, so you shouldn't need to do anything additional when using these
|
53
|
+
files normally.
|
54
|
+
|
55
|
+
We've kept attribution comments terse, so we ask that you do not actively work
|
56
|
+
to remove them from files, especially code. They're a great way for folks to
|
57
|
+
learn about Font Awesome.
|
58
|
+
|
59
|
+
# Brand Icons
|
60
|
+
|
61
|
+
All brand icons are trademarks of their respective owners. The use of these
|
62
|
+
trademarks does not indicate endorsement of the trademark holder by Font
|
63
|
+
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
|
64
|
+
to represent the company, product, or service to which they refer.**
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|

|
2
2
|
|
3
|
-
[](https://github.com/vinistock/sail/actions) [](https://github.com/vinistock/sail/actions) [](https://badge.fury.io/rb/sail) [](https://github.com/markets/awesome-ruby)
|
4
4
|
|
5
5
|
# Sail
|
6
6
|
|
@@ -14,7 +14,7 @@ Enable/Disable a new feature, turn ON/OFF ab testing for new functionality, chan
|
|
14
14
|
|
15
15
|
It comes with a lightweight responsive admin dashboard for searching and changing configurations on the fly.
|
16
16
|
|
17
|
-
Sail assigns to each setting a
|
17
|
+
Sail assigns to each setting a _relevancy score_. This metric is calculated while the application is running and is based on the relative number of times a setting is invoked and on the total number of settings. The goal is to have an indicator of how critical changing a setting's value can be based on its frequency of usage.
|
18
18
|
|
19
19
|
## Contents
|
20
20
|
|
@@ -27,13 +27,15 @@ Sail assigns to each setting a *relevancy score*. This metric is calculated whil
|
|
27
27
|
7. [Contributing](#contributing)
|
28
28
|
|
29
29
|
## Installation
|
30
|
+
|
30
31
|
Add this line to your application's Gemfile:
|
31
32
|
|
32
33
|
```ruby
|
33
|
-
gem
|
34
|
+
gem "sail"
|
34
35
|
```
|
35
36
|
|
36
37
|
And then execute:
|
38
|
+
|
37
39
|
```bash
|
38
40
|
$ bundle
|
39
41
|
```
|
@@ -41,19 +43,25 @@ $ bundle
|
|
41
43
|
Adding the following line to your routes file will make the dashboard available at <base_url>/sail
|
42
44
|
|
43
45
|
```ruby
|
44
|
-
mount Sail::Engine =>
|
46
|
+
mount Sail::Engine => "/sail"
|
45
47
|
```
|
46
48
|
|
47
49
|
Running the install generator will create necessary migrations for having the settings in your database.
|
48
50
|
|
49
51
|
```bash
|
50
|
-
$ rails g sail:install
|
52
|
+
$ bin/rails g sail:install
|
51
53
|
```
|
52
54
|
|
53
55
|
When going through a major version upgrade, be sure to check the [changelog] and run the update generator. It will create whatever migrations are needed to move from any other major version to the latest.
|
54
56
|
|
55
57
|
```bash
|
56
|
-
$ rails g sail:update
|
58
|
+
$ bin/rails g sail:update
|
59
|
+
```
|
60
|
+
|
61
|
+
If you wish to customize the settings' card, the views can be copied to the main app by using the view generator.
|
62
|
+
|
63
|
+
```bash
|
64
|
+
$ bin/rails g sail:views
|
57
65
|
```
|
58
66
|
|
59
67
|
## Configuration
|
@@ -63,9 +71,9 @@ Available configurations and their defaults are listed below
|
|
63
71
|
```ruby
|
64
72
|
Sail.configure do |config|
|
65
73
|
config.cache_life_span = 6.hours # How long to cache the Sail.get response for (note that cache is deleted after a set)
|
66
|
-
config.array_separator =
|
74
|
+
config.array_separator = ";" # Default separator for array settings
|
67
75
|
config.dashboard_auth_lambda = nil # Defines an authorization lambda to access the dashboard as a before action. Rendering or redirecting is included here if desired.
|
68
|
-
config.back_link_path =
|
76
|
+
config.back_link_path = "root_path" # Path method as string for the "Main app" button in the dashboard. Any non-existent path will make the button disappear
|
69
77
|
config.enable_search_auto_submit = true # Enables search auto submit after 2 seconds without typing
|
70
78
|
config.days_until_stale = 60 # Days with no updates until a setting is considered stale and is a candidate to be removed from code (leave nil to disable checks)
|
71
79
|
config.enable_logging = true # Enable logging for update and reset actions. Logs include timestamp, setting name, new value and author_user_id (if current_user is defined)
|
@@ -96,22 +104,23 @@ After settings have been created a first time, they will not be updated with the
|
|
96
104
|
Removing the entries from this file will cause settings to be deleted from the database.
|
97
105
|
|
98
106
|
Settings can be aggregated by using groups. Searching by a group name will return all settings for that group.
|
107
|
+
|
99
108
|
```yaml
|
100
109
|
# Rails.root/config/sail.yml
|
101
110
|
# Setting name with it's information contained inside
|
102
111
|
# These values are used for the reset functionality as well
|
103
112
|
|
104
113
|
first_setting:
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
114
|
+
description: My very first setting
|
115
|
+
value: some_important_string
|
116
|
+
cast_type: string
|
117
|
+
group: setting_group_1
|
109
118
|
second_setting:
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
```
|
119
|
+
description: My second setting, this time a boolean
|
120
|
+
value: false
|
121
|
+
cast_type: boolean
|
122
|
+
group: feature_flags
|
123
|
+
```
|
115
124
|
|
116
125
|
To clear the database and reload the contents of your sail.yml file, invoke this rake task.
|
117
126
|
|
@@ -123,11 +132,11 @@ $ rake sail:load_defaults
|
|
123
132
|
|
124
133
|
Searching for settings in the dashboard can be done in the following ways:
|
125
134
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
135
|
+
- By name: matches a substring of the setting's name
|
136
|
+
- By group: matches all settings with the same group (exact match)
|
137
|
+
- By cast type: matches all settings with the same cast type (exact match)
|
138
|
+
- By stale: type 'stale' and get all settings that haven't been updated in X days (X is defined in the configuration)
|
139
|
+
- By recent: type 'recent X' where X is the number of hours and get all settings that have been updated since X hours ago
|
131
140
|
|
132
141
|
## Manipulating settings in the code
|
133
142
|
|
@@ -152,9 +161,9 @@ end
|
|
152
161
|
# failures_until_reset, it will automatically trigger a Sail.reset for that setting.
|
153
162
|
|
154
163
|
# For example, this will ignore ExampleError, but any other error raised will increase
|
155
|
-
# the count until the setting "name" is reset.
|
164
|
+
# the count until the setting "name" is reset.
|
156
165
|
Sail.get(:name, expected_errors: [ExampleError]) do |value|
|
157
|
-
code_that_can_raise_example_error(value)
|
166
|
+
code_that_can_raise_example_error(value)
|
158
167
|
end
|
159
168
|
|
160
169
|
# Set setting value
|
@@ -169,7 +178,7 @@ Sail.reset(:name)
|
|
169
178
|
# negative: This is the name of the setting that will be returned if the throttle setting returns false
|
170
179
|
# throttle: A setting of cast_type throttle that will switch between positive and negative
|
171
180
|
#
|
172
|
-
# return: Value with cast of either positive or negative, depending on the randomized value of throttle
|
181
|
+
# return: Value with cast of either positive or negative, depending on the randomized value of throttle
|
173
182
|
# Settings positive and negative do not have to be of the same type. However, throttle must be a throttle type setting
|
174
183
|
|
175
184
|
Sail.switcher(
|
@@ -236,14 +245,18 @@ To query settings via GraphQL, use the following pattern.
|
|
236
245
|
```graphql
|
237
246
|
query {
|
238
247
|
sailGet(name: "my_setting")
|
239
|
-
sailSwitcher(
|
248
|
+
sailSwitcher(
|
249
|
+
positive: "positive_case_setting"
|
250
|
+
negative: "negative_case_setting"
|
251
|
+
throttledBy: "throttle_setting"
|
252
|
+
)
|
240
253
|
}
|
241
254
|
|
242
255
|
mutation {
|
243
256
|
sailSet(name: "my_setting", value: "value") {
|
244
257
|
success
|
245
258
|
}
|
246
|
-
|
259
|
+
|
247
260
|
sailProfileSwitch(name: "my_profile") {
|
248
261
|
success
|
249
262
|
}
|
@@ -252,10 +265,14 @@ mutation {
|
|
252
265
|
|
253
266
|
## Localization
|
254
267
|
|
255
|
-
Sail's few strings are all localized for English in [en.yml]
|
268
|
+
Sail's few strings are all localized for English in [en.yml]. Using the same YAML keys for other languages should work for localizing the dashboard.
|
256
269
|
|
257
270
|
Make sure to pass in the desired locale as a parameter.
|
258
271
|
|
272
|
+
## Credits
|
273
|
+
|
274
|
+
The **awesome** icons used by Sail's admin panel are all made by [Font Awesome](https://github.com/FortAwesome/Font-Awesome).
|
275
|
+
|
259
276
|
## Contributing
|
260
277
|
|
261
278
|
Contributions are very welcome! Don't hesitate to ask if you wish to contribute, but don't yet know how.
|
data/Rakefile
CHANGED
@@ -16,12 +16,12 @@ Dir[File.join(File.dirname(__FILE__), "tasks/**/*.rake")].each { |f| load f }
|
|
16
16
|
|
17
17
|
require "rspec/core"
|
18
18
|
require "rspec/core/rake_task"
|
19
|
+
require "rubocop/rake_task"
|
19
20
|
|
20
21
|
RSpec::Core::RakeTask.new(spec: "app:db:test:prepare")
|
21
|
-
task default: :spec
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
RuboCop::RakeTask.new
|
24
|
+
|
25
|
+
task default: %i[spec rubocop]
|
26
26
|
|
27
27
|
system("cd ./spec/dummy; RAILS_ENV=test rails db:environment:set; cd ../..")
|
@@ -1 +1 @@
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M31.7 239l136-136c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9L127.9 256l96.4 96.4c9.4 9.4 9.4 24.6 0 33.9L201.7 409c-9.4 9.4-24.6 9.4-33.9 0l-136-136c-9.5-9.4-9.5-24.6-.1-34z"/></svg>
|
@@ -1 +1 @@
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z"/></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"/></svg>
|
@@ -1 +1 @@
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"/></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M432,320H400a16,16,0,0,0-16,16V448H64V128H208a16,16,0,0,0,16-16V80a16,16,0,0,0-16-16H48A48,48,0,0,0,0,112V464a48,48,0,0,0,48,48H400a48,48,0,0,0,48-48V336A16,16,0,0,0,432,320ZM488,0h-128c-21.37,0-32.05,25.91-17,41l35.73,35.73L135,320.37a24,24,0,0,0,0,34L157.67,377a24,24,0,0,0,34,0L435.28,133.32,471,169c15,15,41,4.5,41-17V24A24,24,0,0,0,488,0Z"/></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M496 384H160v-16c0-8.8-7.2-16-16-16h-32c-8.8 0-16 7.2-16 16v16H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h80v16c0 8.8 7.2 16 16 16h32c8.8 0 16-7.2 16-16v-16h336c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-160h-80v-16c0-8.8-7.2-16-16-16h-32c-8.8 0-16 7.2-16 16v16H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h336v16c0 8.8 7.2 16 16 16h32c8.8 0 16-7.2 16-16v-16h80c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-160H288V48c0-8.8-7.2-16-16-16h-32c-8.8 0-16 7.2-16 16v16H16C7.2 64 0 71.2 0 80v32c0 8.8 7.2 16 16 16h208v16c0 8.8 7.2 16 16 16h32c8.8 0 16-7.2 16-16v-16h208c8.8 0 16-7.2 16-16V80c0-8.8-7.2-16-16-16z"/></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"/></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M212.333 224.333H12c-6.627 0-12-5.373-12-12V12C0 5.373 5.373 0 12 0h48c6.627 0 12 5.373 12 12v78.112C117.773 39.279 184.26 7.47 258.175 8.007c136.906.994 246.448 111.623 246.157 248.532C504.041 393.258 393.12 504 256.333 504c-64.089 0-122.496-24.313-166.51-64.215-5.099-4.622-5.334-12.554-.467-17.42l33.967-33.967c4.474-4.474 11.662-4.717 16.401-.525C170.76 415.336 211.58 432 256.333 432c97.268 0 176-78.716 176-176 0-97.267-78.716-176-176-176-58.496 0-110.28 28.476-142.274 72.333h98.274c6.627 0 12 5.373 12 12v48c0 6.627-5.373 12-12 12z"/></svg>
|
@@ -38,17 +38,15 @@ module Sail
|
|
38
38
|
def switcher
|
39
39
|
respond_to do |format|
|
40
40
|
format.json do
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
head(:not_found)
|
51
|
-
end
|
41
|
+
render json: {
|
42
|
+
value: Sail::Setting.switcher(positive: s_params[:positive],
|
43
|
+
negative: s_params[:negative],
|
44
|
+
throttled_by: s_params[:throttled_by])
|
45
|
+
}
|
46
|
+
rescue Sail::Setting::UnexpectedCastType
|
47
|
+
head(:bad_request)
|
48
|
+
rescue ActiveRecord::RecordNotFound
|
49
|
+
head(:not_found)
|
52
50
|
end
|
53
51
|
end
|
54
52
|
end
|
data/app/models/sail/entry.rb
CHANGED
@@ -10,7 +10,7 @@ module Sail
|
|
10
10
|
class Entry < ApplicationRecord
|
11
11
|
belongs_to :setting
|
12
12
|
belongs_to :profile
|
13
|
-
validates :value,
|
13
|
+
validates :value, presence: true
|
14
14
|
|
15
15
|
scope :by_profile_name, ->(name) { joins(:profile).where(sail_profiles: { name: name }) }
|
16
16
|
|
data/app/models/sail/setting.rb
CHANGED
@@ -134,7 +134,7 @@ module Sail
|
|
134
134
|
attributes[setting.name] = setting_attrs.merge("cast_type" => setting.cast_type)
|
135
135
|
end
|
136
136
|
|
137
|
-
File.
|
137
|
+
File.write(config_file_path, attributes.to_yaml)
|
138
138
|
end
|
139
139
|
|
140
140
|
private_class_method :config_file_path, :destroy_missing_settings,
|
@@ -162,6 +162,13 @@ module Sail
|
|
162
162
|
try(:caster).try(:to_value)
|
163
163
|
end
|
164
164
|
|
165
|
+
# cache_index
|
166
|
+
#
|
167
|
+
# Used in _setting.html.erb for the cache_key
|
168
|
+
def cache_index
|
169
|
+
Sail.instrumenter[name][:usages] / Instrumenter::USAGES_UNTIL_CACHE_EXPIRE
|
170
|
+
end
|
171
|
+
|
165
172
|
private
|
166
173
|
|
167
174
|
def instantiate_caster
|
@@ -14,7 +14,7 @@
|
|
14
14
|
</div>
|
15
15
|
|
16
16
|
<button id="btn-order" class="search-button" title="<%= I18n.t("sail.order_button_tooltip") %>">
|
17
|
-
<%= image_tag("sail/
|
17
|
+
<%= image_tag("sail/sliders-h.svg", alt: I18n.t("sail.order_button_tooltip")) %>
|
18
18
|
</button>
|
19
19
|
<%= render(partial: "sort_menu") %>
|
20
20
|
|
@@ -24,7 +24,7 @@
|
|
24
24
|
|
25
25
|
<% if main_app.respond_to?(Sail.configuration.back_link_path.to_s) %>
|
26
26
|
<%= link_to(main_app.method(Sail.configuration.back_link_path).call, method: :get, id: "main-app-link", class: "search-button", title: I18n.t("sail.main_app")) do %>
|
27
|
-
<%= image_tag("sail/link.svg", alt: I18n.t("sail.main_app")) %>
|
27
|
+
<%= image_tag("sail/external-link-alt.svg", alt: I18n.t("sail.main_app")) %>
|
28
28
|
<% end %>
|
29
29
|
<% end %>
|
30
30
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<% cache setting, expires_in: Sail.configuration.cache_life_span do %>
|
1
|
+
<% cache [setting, setting.cache_index], expires_in: Sail.configuration.cache_life_span do %>
|
2
2
|
<div class="card">
|
3
3
|
<div class="front">
|
4
4
|
<h3 class="title card-title"><%= setting.display_name %></h3>
|
@@ -10,7 +10,7 @@
|
|
10
10
|
<div>
|
11
11
|
<%= form_tag(reset_setting_path(name: setting.name), method: :put, remote: true) do %>
|
12
12
|
<button class="refresh-button" title="<%= I18n.t("sail.refresh_tooltip") %>">
|
13
|
-
<%= image_tag("sail/
|
13
|
+
<%= image_tag("sail/undo.svg", alt: I18n.t("sail.refresh_tooltip")) %>
|
14
14
|
</button>
|
15
15
|
<% end %>
|
16
16
|
</div>
|
@@ -65,11 +65,11 @@
|
|
65
65
|
<button id="btn-submit-<%= setting.name %>" type="submit" class="btn-value-submit" disabled><%= I18n.t("sail.save") %></button>
|
66
66
|
|
67
67
|
<span id="success-<%= setting.name %>" class="notice success">
|
68
|
-
<%= image_tag("sail/
|
68
|
+
<%= image_tag("sail/check.svg") %>
|
69
69
|
</span>
|
70
70
|
|
71
71
|
<span id="alert-<%= setting.name %>" class="notice alert">
|
72
|
-
<%= image_tag("sail/
|
72
|
+
<%= image_tag("sail/times.svg") %>
|
73
73
|
</span>
|
74
74
|
</div>
|
75
75
|
</div>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sail
|
4
|
+
module Generators
|
5
|
+
# ViewsGenerator
|
6
|
+
# Copies the customizable views to the main app
|
7
|
+
class ViewsGenerator < ::Rails::Generators::Base
|
8
|
+
source_root File.expand_path("../../../../app/views/sail", __dir__)
|
9
|
+
desc "Copies customizable views to the parent application."
|
10
|
+
|
11
|
+
def copy_views
|
12
|
+
copy_file("settings/_setting.html.erb", "app/views/sail/settings/_setting.html.erb")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/sail/engine.rb
CHANGED
@@ -22,15 +22,15 @@ module Sail
|
|
22
22
|
config.middleware.use Rack::ETag
|
23
23
|
|
24
24
|
initializer "sail.assets.precompile" do |app|
|
25
|
-
app.config.assets.precompile += %w[sail/
|
26
|
-
sail/angle-right.svg sail/link.svg sail/cog.svg sail/
|
27
|
-
sail/
|
25
|
+
app.config.assets.precompile += %w[sail/undo.svg sail/sliders-h.svg sail/angle-left.svg
|
26
|
+
sail/angle-right.svg sail/external-link-alt.svg sail/cog.svg sail/check.svg
|
27
|
+
sail/times.svg sail/application.css sail/application.js]
|
28
28
|
end
|
29
29
|
|
30
30
|
initializer "sail" do
|
31
31
|
unless Sail.configuration.dashboard_auth_lambda.nil?
|
32
|
-
to_prepare do
|
33
|
-
Sail::SettingsController.before_action(
|
32
|
+
ActiveSupport::Reloader.to_prepare do
|
33
|
+
Sail::SettingsController.before_action(Sail.configuration.dashboard_auth_lambda)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -47,12 +47,5 @@ module Sail
|
|
47
47
|
warn "Skipping setting creation because database doesn't exist"
|
48
48
|
end
|
49
49
|
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
def to_prepare(&block)
|
54
|
-
klass = defined?(ActiveSupport::Reloader) ? ActiveSupport::Reloader : ActionDispatch::Reloader
|
55
|
-
klass.to_prepare(&block)
|
56
|
-
end
|
57
50
|
end
|
58
51
|
end
|
data/lib/sail/instrumenter.rb
CHANGED
@@ -49,7 +49,6 @@ module Sail
|
|
49
49
|
# times a setting has been called
|
50
50
|
def increment_usage_of(setting_name)
|
51
51
|
self[setting_name][:usages] += 1
|
52
|
-
expire_cache_fragment(setting_name) if (self[setting_name][:usages] % USAGES_UNTIL_CACHE_EXPIRE).zero?
|
53
52
|
end
|
54
53
|
|
55
54
|
# relative_usage_of
|
@@ -60,7 +59,10 @@ module Sail
|
|
60
59
|
def relative_usage_of(setting_name)
|
61
60
|
return 0.0 if @statistics[:settings].empty?
|
62
61
|
|
63
|
-
|
62
|
+
total_usages = @statistics[:settings].sum { |_, entry| entry[:usages] }
|
63
|
+
return 0.0 if total_usages.zero?
|
64
|
+
|
65
|
+
(100.0 * self[setting_name][:usages]) / total_usages
|
64
66
|
end
|
65
67
|
|
66
68
|
# increment_failure_of
|
@@ -80,11 +82,5 @@ module Sail
|
|
80
82
|
def relevancy_of(setting_name)
|
81
83
|
(relative_usage_of(setting_name) / @number_of_settings).round(1)
|
82
84
|
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
def expire_cache_fragment(setting_name)
|
87
|
-
ActionController::Base.new.expire_fragment(/name: "#{setting_name}"/)
|
88
|
-
end
|
89
85
|
end
|
90
86
|
end
|
data/lib/sail/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.6.
|
4
|
+
version: 3.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vinicius Stock
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fugit
|
@@ -38,216 +38,6 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 5.0.0
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: brakeman
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: bundler
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: capybara
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: capybara-selenium
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: database_cleaner
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: rack-mini-profiler
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: rspec-rails
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - ">="
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: '0'
|
132
|
-
type: :development
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - ">="
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: '0'
|
139
|
-
- !ruby/object:Gem::Dependency
|
140
|
-
name: rspec-retry
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
142
|
-
requirements:
|
143
|
-
- - ">="
|
144
|
-
- !ruby/object:Gem::Version
|
145
|
-
version: '0'
|
146
|
-
type: :development
|
147
|
-
prerelease: false
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
149
|
-
requirements:
|
150
|
-
- - ">="
|
151
|
-
- !ruby/object:Gem::Version
|
152
|
-
version: '0'
|
153
|
-
- !ruby/object:Gem::Dependency
|
154
|
-
name: rubocop
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
|
-
requirements:
|
157
|
-
- - ">="
|
158
|
-
- !ruby/object:Gem::Version
|
159
|
-
version: '0'
|
160
|
-
type: :development
|
161
|
-
prerelease: false
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
163
|
-
requirements:
|
164
|
-
- - ">="
|
165
|
-
- !ruby/object:Gem::Version
|
166
|
-
version: '0'
|
167
|
-
- !ruby/object:Gem::Dependency
|
168
|
-
name: rubocop-packaging
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
170
|
-
requirements:
|
171
|
-
- - ">="
|
172
|
-
- !ruby/object:Gem::Version
|
173
|
-
version: '0'
|
174
|
-
type: :development
|
175
|
-
prerelease: false
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
177
|
-
requirements:
|
178
|
-
- - ">="
|
179
|
-
- !ruby/object:Gem::Version
|
180
|
-
version: '0'
|
181
|
-
- !ruby/object:Gem::Dependency
|
182
|
-
name: rubocop-performance
|
183
|
-
requirement: !ruby/object:Gem::Requirement
|
184
|
-
requirements:
|
185
|
-
- - ">="
|
186
|
-
- !ruby/object:Gem::Version
|
187
|
-
version: '0'
|
188
|
-
type: :development
|
189
|
-
prerelease: false
|
190
|
-
version_requirements: !ruby/object:Gem::Requirement
|
191
|
-
requirements:
|
192
|
-
- - ">="
|
193
|
-
- !ruby/object:Gem::Version
|
194
|
-
version: '0'
|
195
|
-
- !ruby/object:Gem::Dependency
|
196
|
-
name: rubocop-rails
|
197
|
-
requirement: !ruby/object:Gem::Requirement
|
198
|
-
requirements:
|
199
|
-
- - ">="
|
200
|
-
- !ruby/object:Gem::Version
|
201
|
-
version: '0'
|
202
|
-
type: :development
|
203
|
-
prerelease: false
|
204
|
-
version_requirements: !ruby/object:Gem::Requirement
|
205
|
-
requirements:
|
206
|
-
- - ">="
|
207
|
-
- !ruby/object:Gem::Version
|
208
|
-
version: '0'
|
209
|
-
- !ruby/object:Gem::Dependency
|
210
|
-
name: simplecov
|
211
|
-
requirement: !ruby/object:Gem::Requirement
|
212
|
-
requirements:
|
213
|
-
- - "~>"
|
214
|
-
- !ruby/object:Gem::Version
|
215
|
-
version: 0.21.0
|
216
|
-
type: :development
|
217
|
-
prerelease: false
|
218
|
-
version_requirements: !ruby/object:Gem::Requirement
|
219
|
-
requirements:
|
220
|
-
- - "~>"
|
221
|
-
- !ruby/object:Gem::Version
|
222
|
-
version: 0.21.0
|
223
|
-
- !ruby/object:Gem::Dependency
|
224
|
-
name: sqlite3
|
225
|
-
requirement: !ruby/object:Gem::Requirement
|
226
|
-
requirements:
|
227
|
-
- - ">="
|
228
|
-
- !ruby/object:Gem::Version
|
229
|
-
version: '0'
|
230
|
-
type: :development
|
231
|
-
prerelease: false
|
232
|
-
version_requirements: !ruby/object:Gem::Requirement
|
233
|
-
requirements:
|
234
|
-
- - ">="
|
235
|
-
- !ruby/object:Gem::Version
|
236
|
-
version: '0'
|
237
|
-
- !ruby/object:Gem::Dependency
|
238
|
-
name: webdrivers
|
239
|
-
requirement: !ruby/object:Gem::Requirement
|
240
|
-
requirements:
|
241
|
-
- - ">="
|
242
|
-
- !ruby/object:Gem::Version
|
243
|
-
version: '0'
|
244
|
-
type: :development
|
245
|
-
prerelease: false
|
246
|
-
version_requirements: !ruby/object:Gem::Requirement
|
247
|
-
requirements:
|
248
|
-
- - ">="
|
249
|
-
- !ruby/object:Gem::Version
|
250
|
-
version: '0'
|
251
41
|
description: Sail is a lightweight Rails engine that brings an admin panel for managing
|
252
42
|
configuration settings on a live Rails app.
|
253
43
|
email:
|
@@ -262,12 +52,12 @@ files:
|
|
262
52
|
- app/assets/config/sail_manifest.js
|
263
53
|
- app/assets/images/sail/angle-left.svg
|
264
54
|
- app/assets/images/sail/angle-right.svg
|
265
|
-
- app/assets/images/sail/
|
55
|
+
- app/assets/images/sail/check.svg
|
266
56
|
- app/assets/images/sail/cog.svg
|
267
|
-
- app/assets/images/sail/
|
268
|
-
- app/assets/images/sail/
|
269
|
-
- app/assets/images/sail/
|
270
|
-
- app/assets/images/sail/
|
57
|
+
- app/assets/images/sail/external-link-alt.svg
|
58
|
+
- app/assets/images/sail/sliders-h.svg
|
59
|
+
- app/assets/images/sail/times.svg
|
60
|
+
- app/assets/images/sail/undo.svg
|
271
61
|
- app/assets/javascripts/sail/application.js
|
272
62
|
- app/assets/javascripts/sail/settings.js
|
273
63
|
- app/assets/stylesheets/sail/application.css.erb
|
@@ -301,6 +91,7 @@ files:
|
|
301
91
|
- lib/generators/sail/install/templates/sail.yml.tt
|
302
92
|
- lib/generators/sail/update/templates/add_group_to_sail_settings.rb
|
303
93
|
- lib/generators/sail/update/update_generator.rb
|
94
|
+
- lib/generators/sail/views/views_generator.rb
|
304
95
|
- lib/sail.rb
|
305
96
|
- lib/sail/configuration.rb
|
306
97
|
- lib/sail/constant_collection.rb
|
@@ -331,7 +122,8 @@ files:
|
|
331
122
|
homepage: https://github.com/vinistock/sail
|
332
123
|
licenses:
|
333
124
|
- MIT
|
334
|
-
metadata:
|
125
|
+
metadata:
|
126
|
+
rubygems_mfa_required: 'true'
|
335
127
|
post_install_message:
|
336
128
|
rdoc_options: []
|
337
129
|
require_paths:
|
@@ -347,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
347
139
|
- !ruby/object:Gem::Version
|
348
140
|
version: '0'
|
349
141
|
requirements: []
|
350
|
-
rubygems_version: 3.
|
142
|
+
rubygems_version: 3.3.4
|
351
143
|
signing_key:
|
352
144
|
specification_version: 4
|
353
145
|
summary: Sail is a lightweight Rails engine that brings an admin panel for managing
|
@@ -1 +0,0 @@
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M0 11l2-2 5 5L18 3l2 2L7 18z"/></svg>
|
@@ -1 +0,0 @@
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M10 8.586L2.929 1.515 1.515 2.929 8.586 10l-7.071 7.071 1.414 1.414L10 11.414l7.071 7.071 1.414-1.414L11.414 10l7.071-7.071-1.414-1.414L10 8.586z"/></svg>
|
@@ -1 +0,0 @@
|
|
1
|
-
<svg aria-hidden="true" data-prefix="fas" data-icon="external-link-alt" class="svg-inline--fa fa-external-link-alt fa-w-18" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M576 24v127.984c0 21.461-25.96 31.98-40.971 16.971l-35.707-35.709-243.523 243.523c-9.373 9.373-24.568 9.373-33.941 0l-22.627-22.627c-9.373-9.373-9.373-24.569 0-33.941L442.756 76.676l-35.703-35.705C391.982 25.9 402.656 0 424.024 0H552c13.255 0 24 10.745 24 24zM407.029 270.794l-16 16A23.999 23.999 0 0 0 384 303.765V448H64V128h264a24.003 24.003 0 0 0 16.97-7.029l16-16C376.089 89.851 365.381 64 344 64H48C21.49 64 0 85.49 0 112v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V287.764c0-21.382-25.852-32.09-40.971-16.97z"></path></svg>
|
@@ -1 +0,0 @@
|
|
1
|
-
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="undo" class="svg-inline--fa fa-undo fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M212.333 224.333H12c-6.627 0-12-5.373-12-12V12C0 5.373 5.373 0 12 0h48c6.627 0 12 5.373 12 12v78.112C117.773 39.279 184.26 7.47 258.175 8.007c136.906.994 246.448 111.623 246.157 248.532C504.041 393.258 393.12 504 256.333 504c-64.089 0-122.496-24.313-166.51-64.215-5.099-4.622-5.334-12.554-.467-17.42l33.967-33.967c4.474-4.474 11.662-4.717 16.401-.525C170.76 415.336 211.58 432 256.333 432c97.268 0 176-78.716 176-176 0-97.267-78.716-176-176-176-58.496 0-110.28 28.476-142.274 72.333h98.274c6.627 0 12 5.373 12 12v48c0 6.627-5.373 12-12 12z"></path></svg>
|
@@ -1 +0,0 @@
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17 16v4h-2v-4h-2v-3h6v3h-2zM1 9h6v3H1V9zm6-4h6v3H7V5zM3 0h2v8H3V0zm12 0h2v12h-2V0zM9 0h2v4H9V0zM3 12h2v8H3v-8zm6-4h2v12H9V8z"/></svg>
|