cani 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +45 -0
- data/LICENSE.txt +21 -0
- data/README.md +140 -0
- data/Rakefile +6 -0
- data/bin/console +8 -0
- data/bin/setup +8 -0
- data/cani.gemspec +32 -0
- data/exe/cani +20 -0
- data/lib/cani.rb +95 -0
- data/lib/cani/api.rb +87 -0
- data/lib/cani/api/browser.rb +50 -0
- data/lib/cani/api/config.rb +121 -0
- data/lib/cani/api/feature.rb +54 -0
- data/lib/cani/completions.rb +107 -0
- data/lib/cani/fzf.rb +88 -0
- data/lib/cani/version.rb +3 -0
- data/shell/completions/functions.bash +26 -0
- data/shell/completions/functions.fish +39 -0
- data/shell/completions/functions.zsh +26 -0
- metadata +153 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bbc79d5e5ddb3a2d40c39165c898d32ffa657a68dec23636f1b2b15da5bb9857
|
|
4
|
+
data.tar.gz: b93cf69ff6740b4fbd11010d5d3c05c196eb2e63edcdc5efbe09dba51279ba12
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 688f7a52ab214dfeb46e46a1b93c8ab16e9ecc2fecd8d7e2a2de2c57b4cb0588e088bb24e313e8f9bb08e0e218842ea3b16439983357ad51dca35a778bacf706
|
|
7
|
+
data.tar.gz: eae2981b38417c952878e3dd9fd7309881f07fe39bf726bd7986a74bb5d7a9f053f4453d4e909e12927b8d5d46104570ba0a450781ebff66932ee390ff279990
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -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 sidneyliebrand@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
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
cani (0.1.0)
|
|
5
|
+
colorize
|
|
6
|
+
json
|
|
7
|
+
|
|
8
|
+
GEM
|
|
9
|
+
remote: https://rubygems.org/
|
|
10
|
+
specs:
|
|
11
|
+
coderay (1.1.2)
|
|
12
|
+
colorize (0.8.1)
|
|
13
|
+
diff-lcs (1.3)
|
|
14
|
+
json (2.1.0)
|
|
15
|
+
method_source (0.9.0)
|
|
16
|
+
pry (0.11.3)
|
|
17
|
+
coderay (~> 1.1.0)
|
|
18
|
+
method_source (~> 0.9.0)
|
|
19
|
+
rake (10.5.0)
|
|
20
|
+
rspec (3.7.0)
|
|
21
|
+
rspec-core (~> 3.7.0)
|
|
22
|
+
rspec-expectations (~> 3.7.0)
|
|
23
|
+
rspec-mocks (~> 3.7.0)
|
|
24
|
+
rspec-core (3.7.1)
|
|
25
|
+
rspec-support (~> 3.7.0)
|
|
26
|
+
rspec-expectations (3.7.0)
|
|
27
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
28
|
+
rspec-support (~> 3.7.0)
|
|
29
|
+
rspec-mocks (3.7.0)
|
|
30
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
31
|
+
rspec-support (~> 3.7.0)
|
|
32
|
+
rspec-support (3.7.1)
|
|
33
|
+
|
|
34
|
+
PLATFORMS
|
|
35
|
+
ruby
|
|
36
|
+
|
|
37
|
+
DEPENDENCIES
|
|
38
|
+
bundler (~> 1.16)
|
|
39
|
+
cani!
|
|
40
|
+
pry
|
|
41
|
+
rake (~> 10.0)
|
|
42
|
+
rspec (~> 3.0)
|
|
43
|
+
|
|
44
|
+
BUNDLED WITH
|
|
45
|
+
1.16.2
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Sidney Liebrand
|
|
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,140 @@
|
|
|
1
|
+
# Cani
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Cani is a small command-line wrapper around the data of [caniuse](https://caniuse.com).
|
|
6
|
+
It uses [fzf](https://github.com/junegunn/fzf) to display results.
|
|
7
|
+
This wrapper aims to be easy to use out of the box. To achieve this it ships with completions
|
|
8
|
+
for `bash`, `fish`, and `zsh`. [Caniuse data (1.7MB)](https://github.com/Fyrd/caniuse/blob/master/data.json) is fetched and updated automatically
|
|
9
|
+
on a regular interval together with completions.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Add this line to your application's Gemfile:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
gem 'cani'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
And then execute:
|
|
20
|
+
|
|
21
|
+
$ bundle
|
|
22
|
+
|
|
23
|
+
Or install it yourself as:
|
|
24
|
+
|
|
25
|
+
$ gem install cani
|
|
26
|
+
|
|
27
|
+
## Configuration
|
|
28
|
+
|
|
29
|
+
After installation, running the command (`cani`) for the first time will create some files and directories:
|
|
30
|
+
|
|
31
|
+
- `~/.config/cani/config.yml` - default configuration
|
|
32
|
+
- `~/.config/cani/caniuse.json` - caniuse api data
|
|
33
|
+
- `~/.config/cani/completions/_cani.bash` - bash completion
|
|
34
|
+
- `~/.config/cani/completions/_cani.zsh` - zsh completion
|
|
35
|
+
- `~/.config/fish/completions/cani.fish` - fish completions
|
|
36
|
+
|
|
37
|
+
Some existing files will also be modified:
|
|
38
|
+
|
|
39
|
+
- `~/.bashrc` - A source line to bash completions will be added, or updated if it exists
|
|
40
|
+
- `~/.zshrc` - A source line to zsh completions will be added, or updated if it exists
|
|
41
|
+
|
|
42
|
+
After running the command for the first time, please restart your shell or `source` your `~/.*rc` file to load completions.
|
|
43
|
+
There are some commented settings that can be adjusted in the `~/.config/cani/config.yml` file.
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
Running `cani` without arguments yields the help description.
|
|
48
|
+
Cani supports the following actions:
|
|
49
|
+
|
|
50
|
+
- [`use`](#use) - show browser support for all features
|
|
51
|
+
- [`show BROWSER VERSION`](#show) - show feature support based on selected browser / version
|
|
52
|
+
- [`help`](#help) - show help
|
|
53
|
+
- [`version`](#version) - print the version number
|
|
54
|
+
- [`update`](#update) - force update data and completions
|
|
55
|
+
- [`install_completions`](#install_completions) - install shell completions
|
|
56
|
+
- [`purge`](#purge) - purge files and directories created by `cani`
|
|
57
|
+
|
|
58
|
+
### use
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
cani use
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Show a list of features with fzf. Features are shown with their current W3C status, percentage of support, title and
|
|
65
|
+
each individual browser's support on a single row.
|
|
66
|
+
|
|
67
|
+
### show
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
cani show
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Show a list of browsers. Selecting a browser will take you to the versions for that browser.
|
|
74
|
+
Selecting a version shows the final window with feature support for that specific browser version.
|
|
75
|
+
|
|
76
|
+
This command can also be invoked directly with a browser and version:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
# show all versions of chrome
|
|
80
|
+
cani show chr
|
|
81
|
+
|
|
82
|
+
# show all supported features in chrome 70
|
|
83
|
+
cani show chr 70
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### help
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
cani help
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Displays short help for the `cani` command
|
|
93
|
+
|
|
94
|
+
### version
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
cani version
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Displays the current version e.g: `0.1.0`
|
|
101
|
+
|
|
102
|
+
### update
|
|
103
|
+
|
|
104
|
+
```sh
|
|
105
|
+
cani update
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Force update dataset and completions.
|
|
109
|
+
|
|
110
|
+
### install_completions
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
cani install_completions
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Completions are supported for `zsh`, `bash` and `fish` shells (currently).
|
|
117
|
+
They are automatically installed upon first invocation of the `cani` command.
|
|
118
|
+
This command is only a fallback in case there were any issues with permissions etc..
|
|
119
|
+
|
|
120
|
+
### purge
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
cani purge
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Purges all files created by this command, removing every trace except the executable itself.
|
|
127
|
+
It will also remove source lines added that pointed to the completions in `~/.zshrc` and `~/.bashrc`.
|
|
128
|
+
After running a `purge`, all that remains is running `gem uninstall cani` to completely purge it.
|
|
129
|
+
|
|
130
|
+
## Contributing
|
|
131
|
+
|
|
132
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sidofc/cani. 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.
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
137
|
+
|
|
138
|
+
## Code of Conduct
|
|
139
|
+
|
|
140
|
+
Everyone interacting in the Cani project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/cani/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/bin/console
ADDED
data/bin/setup
ADDED
data/cani.gemspec
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require 'cani/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'cani'
|
|
7
|
+
spec.version = Cani::VERSION
|
|
8
|
+
spec.authors = ['Sidney Liebrand']
|
|
9
|
+
spec.email = ['sidneyliebrand@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'A simple caniuse CLI.'
|
|
12
|
+
spec.description = 'A rework of the ruby script from my medium post: https://medium.com/@sidneyliebrand/combining-caniuse-with-fzf-fb93ad235bae'
|
|
13
|
+
spec.homepage = 'https://github.com/SidOfc/cani'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
|
|
16
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
|
17
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
18
|
+
f.match(%r{^(test|spec|features|assets)/})
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
spec.bindir = 'exe'
|
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
23
|
+
spec.require_paths = ['lib']
|
|
24
|
+
|
|
25
|
+
spec.add_runtime_dependency 'colorize'
|
|
26
|
+
spec.add_runtime_dependency 'json'
|
|
27
|
+
|
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
|
29
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
|
30
|
+
spec.add_development_dependency 'pry'
|
|
31
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
32
|
+
end
|
data/exe/cani
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'bundler/setup'
|
|
5
|
+
require 'cani'
|
|
6
|
+
|
|
7
|
+
# extract first non-option (options starting with '-' or '--')
|
|
8
|
+
# argument in ARGV.
|
|
9
|
+
cmd ||= Cani.api.config.args.first
|
|
10
|
+
|
|
11
|
+
# refresh all completions after new data has been fetched
|
|
12
|
+
Cani::Completions.install! if Cani.api.updated?
|
|
13
|
+
|
|
14
|
+
if cmd && Cani.respond_to?(cmd)
|
|
15
|
+
# if the command is recognized, execute it
|
|
16
|
+
Cani.send cmd
|
|
17
|
+
else
|
|
18
|
+
# otherwise, display help message
|
|
19
|
+
Cani.help
|
|
20
|
+
end
|
data/lib/cani.rb
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'colorize'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'yaml'
|
|
6
|
+
|
|
7
|
+
require 'cani/version'
|
|
8
|
+
require 'cani/api'
|
|
9
|
+
require 'cani/fzf'
|
|
10
|
+
require 'cani/completions'
|
|
11
|
+
|
|
12
|
+
# Cani
|
|
13
|
+
module Cani
|
|
14
|
+
def self.api
|
|
15
|
+
@api ||= Api.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.help
|
|
19
|
+
puts "Cani #{VERSION} <https://github.com/SidOfc/cani>"
|
|
20
|
+
puts ''
|
|
21
|
+
puts 'Usage: cani [COMMAND [ARGUMENTS]]'
|
|
22
|
+
puts ''
|
|
23
|
+
puts 'Commands:'
|
|
24
|
+
puts ' use FEATURE show browser support for FEATURE'
|
|
25
|
+
puts ' show BROWSER show information about specific BROWSER'
|
|
26
|
+
puts ' install_completions installs completions for bash, zsh and fish'
|
|
27
|
+
puts ' update force update api data and completions'
|
|
28
|
+
puts ' purge remove all completion, configuration and data'
|
|
29
|
+
puts ' stored by this cani'
|
|
30
|
+
puts ' '
|
|
31
|
+
puts ' help show this help'
|
|
32
|
+
puts ' version print the version number'
|
|
33
|
+
puts ''
|
|
34
|
+
puts 'Examples:'
|
|
35
|
+
puts ' cani use'
|
|
36
|
+
puts ' cani show ie'
|
|
37
|
+
puts ' cani show chr.and'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.version
|
|
41
|
+
puts VERSION
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.install_completions
|
|
45
|
+
Completions.install!
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.purge
|
|
49
|
+
Completions.remove!
|
|
50
|
+
api.remove!
|
|
51
|
+
api.config.remove!
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.update
|
|
55
|
+
api.update! && Completions.install! || exit(1) unless api.updated?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.edit
|
|
59
|
+
system ENV.fetch('EDITOR', 'vim'), api.config.file
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.use
|
|
63
|
+
puts Fzf.pick Fzf.feature_rows,
|
|
64
|
+
header: 'use] [' + Api::Feature.support_legend,
|
|
65
|
+
colors: %i[green light_black light_white light_black]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.show(brws = api.config.args[1], version = api.config.args[2])
|
|
69
|
+
browser = api.find_browser brws
|
|
70
|
+
|
|
71
|
+
if browser
|
|
72
|
+
if version
|
|
73
|
+
Fzf.pick Fzf.browser_feature_rows(browser, version),
|
|
74
|
+
header: "show:#{browser.title.downcase}:#{version}] [#{Api::Feature.support_legend}",
|
|
75
|
+
colors: [:green, :light_black, :light_white]
|
|
76
|
+
|
|
77
|
+
show browser.title, nil
|
|
78
|
+
else
|
|
79
|
+
if (version = Fzf.pick(Fzf.browser_usage_rows(browser),
|
|
80
|
+
header: [:show, browser.title],
|
|
81
|
+
colors: %i[white light_black]).first)
|
|
82
|
+
show browser.title, version
|
|
83
|
+
else
|
|
84
|
+
show nil, nil
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
browser = api.find_browser Fzf.pick(Fzf.browser_rows,
|
|
89
|
+
header: [:show],
|
|
90
|
+
colors: %i[white light_black]).first
|
|
91
|
+
|
|
92
|
+
show browser.title, nil unless browser.nil?
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
data/lib/cani/api.rb
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
|
|
3
|
+
require_relative 'api/config'
|
|
4
|
+
require_relative 'api/browser'
|
|
5
|
+
require_relative 'api/feature'
|
|
6
|
+
|
|
7
|
+
module Cani
|
|
8
|
+
class Api
|
|
9
|
+
def initialize
|
|
10
|
+
@data = load_data
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def load_data(fetch: false)
|
|
14
|
+
@upd = false
|
|
15
|
+
data_file = File.join config.directory, 'caniuse.json'
|
|
16
|
+
data_exists = File.exist? data_file
|
|
17
|
+
data_up_to_date = data_exists ? (Time.now.to_i - File.mtime(data_file).to_i < config.expire.to_i)
|
|
18
|
+
: false
|
|
19
|
+
|
|
20
|
+
if !fetch && data_exists && data_up_to_date
|
|
21
|
+
# data is available and up to date
|
|
22
|
+
JSON.parse File.read(data_file)
|
|
23
|
+
elsif (data = raw)
|
|
24
|
+
@upd = true
|
|
25
|
+
# data either doesn't exist or isn't up to date
|
|
26
|
+
# if we can fetch new data, attempt to update
|
|
27
|
+
FileUtils.mkdir_p File.dirname(data_file)
|
|
28
|
+
File.open(data_file, 'w') { |f| f << data }
|
|
29
|
+
JSON.parse data
|
|
30
|
+
elsif data_exists
|
|
31
|
+
# if we are unable fetch new data, fall back
|
|
32
|
+
# to existing data if it exists
|
|
33
|
+
JSON.parse File.read(data_file)
|
|
34
|
+
else
|
|
35
|
+
# no other option but fail since we have no data
|
|
36
|
+
# and no way of fetching the data to display
|
|
37
|
+
puts 'fatal: no data available for display'
|
|
38
|
+
exit 1
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def remove!
|
|
43
|
+
data_file = File.join config.directory, 'caniuse.json'
|
|
44
|
+
|
|
45
|
+
File.unlink data_file if File.exist? data_file
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def update!
|
|
49
|
+
@data = load_data fetch: true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def updated?
|
|
53
|
+
@upd
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def config(**opts)
|
|
57
|
+
@settings ||= Config.new(**opts)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def find_browser(name)
|
|
61
|
+
name = name.to_s.downcase
|
|
62
|
+
idx = browsers.find_index do |bwsr|
|
|
63
|
+
[bwsr.title, bwsr.name, bwsr.abbr].include? name
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
browsers[idx] if idx
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def browsers
|
|
70
|
+
@browsers ||= @data['agents'].map do |(name, info)|
|
|
71
|
+
Browser.new info.merge(name: name)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def features
|
|
76
|
+
@features ||= @data['data'].values.map(&Feature.method(:new))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def raw
|
|
80
|
+
begin
|
|
81
|
+
Net::HTTP.get URI(config.source)
|
|
82
|
+
rescue
|
|
83
|
+
nil
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Cani
|
|
2
|
+
class Api
|
|
3
|
+
class Browser
|
|
4
|
+
attr_reader :name, :title, :prefix, :type, :versions, :usage, :abbr, :label
|
|
5
|
+
|
|
6
|
+
ABBR_MAP = { 'ios' => 'saf.ios' }.freeze
|
|
7
|
+
LABEL_MAP = {
|
|
8
|
+
'ie' => 'Internet Explorer',
|
|
9
|
+
'edge' => 'Edge',
|
|
10
|
+
'ff' => 'Firefox',
|
|
11
|
+
'chr' => 'Chrome',
|
|
12
|
+
'saf' => 'Safari',
|
|
13
|
+
'op' => 'Opera',
|
|
14
|
+
'saf.ios' => 'IOS Safari',
|
|
15
|
+
'o.mini' => 'Opera Mini',
|
|
16
|
+
'and' => 'Android Browser',
|
|
17
|
+
'bb' => 'BlackBerry Browser',
|
|
18
|
+
'o.mob' => 'Opera Mobile',
|
|
19
|
+
'chr.and' => 'Chrome for Android',
|
|
20
|
+
'ff.and' => 'Firefox for Android',
|
|
21
|
+
'ie.mob' => 'Internet Explorer Mobile',
|
|
22
|
+
'uc' => 'UC Browser for android',
|
|
23
|
+
'ss' => 'Samsung Internet',
|
|
24
|
+
'qq' => 'QQ Browser',
|
|
25
|
+
'baidu' => 'Baidu Browser'
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
28
|
+
def initialize(attributes = {})
|
|
29
|
+
abbr = attributes['abbr'].downcase.gsub(/^\.+|\.+$/, '').tr '/', '.'
|
|
30
|
+
|
|
31
|
+
@abbr = ABBR_MAP.fetch abbr, abbr
|
|
32
|
+
@label = LABEL_MAP.fetch abbr, abbr
|
|
33
|
+
@name = attributes[:name].downcase
|
|
34
|
+
@title = attributes['browser'].downcase
|
|
35
|
+
@prefix = attributes['prefix'].downcase
|
|
36
|
+
@type = attributes['type'].downcase
|
|
37
|
+
@usage = attributes['usage_global']
|
|
38
|
+
@versions = @usage.keys
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def features_for(version)
|
|
42
|
+
@features ||= Cani.api.features.each_with_object({}) do |ft, h|
|
|
43
|
+
type = ft.support_in name, version
|
|
44
|
+
(h[type] ||= []) << { support: type, title: ft.title,
|
|
45
|
+
status: ft.status, percent: ft.percent }
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
module Cani
|
|
2
|
+
class Api
|
|
3
|
+
class Config
|
|
4
|
+
attr_reader :settings
|
|
5
|
+
|
|
6
|
+
FILE = File.join(Dir.home, '.config', 'cani', 'config.yml').freeze
|
|
7
|
+
DIRECTORY = File.dirname(FILE).freeze
|
|
8
|
+
COMP_DIR = File.join(DIRECTORY, 'completions').freeze
|
|
9
|
+
FISH_DIR = File.join(Dir.home, '.config', 'fish').freeze
|
|
10
|
+
FISH_COMP_DIR = File.join(FISH_DIR, 'completions').freeze
|
|
11
|
+
DEFAULTS = {
|
|
12
|
+
# data settings
|
|
13
|
+
'expire' => 86_400,
|
|
14
|
+
'source' => 'https://raw.githubusercontent.com/Fyrd/caniuse/master/data.json',
|
|
15
|
+
|
|
16
|
+
# usage settings
|
|
17
|
+
'versions' => 1,
|
|
18
|
+
'browsers' => %w[chrome firefox edge ie safari ios_saf opera android bb]
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
def initialize(**opts)
|
|
22
|
+
@settings = DEFAULTS.merge opts
|
|
23
|
+
|
|
24
|
+
if File.exist? file
|
|
25
|
+
if (yml = YAML.load_file(file))
|
|
26
|
+
@settings.merge! yml
|
|
27
|
+
end
|
|
28
|
+
else
|
|
29
|
+
install!
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def file
|
|
34
|
+
FILE
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def directory
|
|
38
|
+
DIRECTORY
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def comp_dir
|
|
42
|
+
COMP_DIR
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def fish_dir
|
|
46
|
+
FISH_DIR
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def fish_comp_dir
|
|
50
|
+
FISH_COMP_DIR
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def flags
|
|
54
|
+
@flags ||= ARGV.select { |arg| arg.start_with? '-' }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def args
|
|
58
|
+
@args ||= ARGV.reject { |arg| arg.start_with? '-' }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def remove!
|
|
62
|
+
File.unlink file if File.exist? file
|
|
63
|
+
FileUtils.rm_rf directory if Dir.exist? directory
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def install!
|
|
67
|
+
hrs = (DEFAULTS['expire'] / 3600.to_f).round 2
|
|
68
|
+
days = (hrs / 24.to_f).round 2
|
|
69
|
+
wk = (days / 7.to_f).round 2
|
|
70
|
+
mo = (days / 30.to_f).round 2
|
|
71
|
+
tstr = if mo >= 1
|
|
72
|
+
"#{mo == mo.to_i ? mo.to_i : mo} month#{mo != 1 ? 's' : ''}"
|
|
73
|
+
elsif wk >= 1
|
|
74
|
+
"#{wk == wk.to_i ? wk.to_i : wk} week#{wk != 1 ? 's' : ''}"
|
|
75
|
+
elsif days >= 1
|
|
76
|
+
"#{days == days.to_i ? days.to_i : days} day#{days != 1 ? 's' : ''}"
|
|
77
|
+
else
|
|
78
|
+
"#{hrs == hrs.to_i ? hrs.to_i : hrs} hour#{hrs != 1 ? 's' : ''}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
FileUtils.mkdir_p directory
|
|
82
|
+
File.open file, 'w' do |f|
|
|
83
|
+
f << "---\n"
|
|
84
|
+
f << "# this is the default configuration file for the \"Cani\" RubyGem.\n"
|
|
85
|
+
f << "# it contains some options to control what is shown, when new data\n"
|
|
86
|
+
f << "# is fetched, where it should be fetched from.\n"
|
|
87
|
+
f << "# documentation: https://github.com/sidofc/cani\n"
|
|
88
|
+
f << "# rubygems: https://rubygems.org/gems/cani\n\n"
|
|
89
|
+
f << "# the \"expire\" key defines the interval at which new data is\n"
|
|
90
|
+
f << "# fetched from \"source\". It's value is passed in as seconds\n"
|
|
91
|
+
f << "# default value: #{DEFAULTS['expire']} # => #{tstr}\n"
|
|
92
|
+
f << "expire: #{expire}\n\n"
|
|
93
|
+
f << "# the \"source\" key is used to fetch the data required for\n"
|
|
94
|
+
f << "# this command to work.\n"
|
|
95
|
+
f << "source: #{source}\n\n"
|
|
96
|
+
f << "# the \"versions\" key defines how many versions of support\n"
|
|
97
|
+
f << "# will be shown in the \"use\" command\n"
|
|
98
|
+
f << "# e.g. `-ie +edge` becomes `--ie ++edge` when this is set to 2, etc..."
|
|
99
|
+
f << "versions: #{versions}\n\n"
|
|
100
|
+
f << "# the \"browsers\" key defines which browsers are shown\n"
|
|
101
|
+
f << "# in the \"use\" command\n"
|
|
102
|
+
f << "browsers:\n"
|
|
103
|
+
f << " # enabled:\n"
|
|
104
|
+
f << browsers.map { |bn| " - #{bn}" }.join("\n") + "\n"
|
|
105
|
+
f << " # others:\n"
|
|
106
|
+
f << (Cani.api.browsers.map(&:name) - browsers).map { |bn| " # - #{bn}" }.join("\n")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
Completions.install!
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def method_missing(mtd, *args, &block)
|
|
113
|
+
settings.key?(mtd.to_s) ? settings[mtd.to_s] : super
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def respond_to_missing?(mtd, include_private = false)
|
|
117
|
+
settings.key? mtd.to_s
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Cani
|
|
2
|
+
class Api
|
|
3
|
+
class Feature
|
|
4
|
+
attr_reader :title, :status, :spec, :stats, :percent
|
|
5
|
+
|
|
6
|
+
STATUSES = {
|
|
7
|
+
'rec' => 'rc',
|
|
8
|
+
'unoff' => 'un',
|
|
9
|
+
'other' => 'ot'
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
TYPES = {
|
|
13
|
+
'y' => {symbol: '+', name: :default, short: :def},
|
|
14
|
+
'a' => {symbol: '~', name: :partial, short: :part},
|
|
15
|
+
'n' => {symbol: '-', name: :unsupported, short: :unsupp},
|
|
16
|
+
'p' => {symbol: '#', name: :polyfill, short: :poly},
|
|
17
|
+
'x' => {symbol: '@', name: :prefix, short: :prefix},
|
|
18
|
+
'd' => {symbol: '!', name: :flag, short: :flag},
|
|
19
|
+
'u' => {symbol: '?', name: :unknown, short: :unknown}
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
def initialize(attributes = {})
|
|
23
|
+
@title = attributes['title']
|
|
24
|
+
@status = STATUSES.fetch attributes['status'], attributes['status']
|
|
25
|
+
@spec = attributes['spec']
|
|
26
|
+
@percent = attributes['usage_perc_y']
|
|
27
|
+
@stats = attributes['stats'].each_with_object({}) do |(k, v), h|
|
|
28
|
+
h[k] = v.to_a.last(Cani.api.config.versions)
|
|
29
|
+
.map { |(vv, s)| [vv.downcase, s.to_s[0] || ''] }.to_h
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def current_support
|
|
34
|
+
@current_support ||= Cani.api.config.browsers.map do |browser|
|
|
35
|
+
bridx = Cani.api.browsers.find_index { |brs| brs.name == browser }
|
|
36
|
+
brwsr = Cani.api.browsers[bridx] unless bridx.nil?
|
|
37
|
+
syms = stats[browser].values.map { |s| TYPES[s][:symbol] || '' }
|
|
38
|
+
.join.rjust Cani.api.config.versions
|
|
39
|
+
|
|
40
|
+
syms + brwsr.abbr
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def support_in(browser, version)
|
|
45
|
+
TYPES.fetch(stats[browser.to_s][version.to_s], {})
|
|
46
|
+
.fetch :name, :unknown
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.support_legend
|
|
50
|
+
TYPES.map { |_, v| "#{v[:short]}(#{v[:symbol]})" }.join ' '
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
module Cani
|
|
2
|
+
module Completions
|
|
3
|
+
def self.generate_fish
|
|
4
|
+
gem_root = File.join File.dirname(__FILE__), '../../'
|
|
5
|
+
tpl = File.read File.join(gem_root, 'shell/completions/functions.fish')
|
|
6
|
+
shw = Cani.api.browsers.reduce String.new do |acc, browser|
|
|
7
|
+
[acc, "complete -f -c cani -n '__fish_cani_using_command show' -a '#{browser.abbr}' -d '#{browser.label}'",
|
|
8
|
+
"complete -f -c cani -n '__fish_cani_showing_browser #{browser.abbr}' -a '#{browser.versions.reverse.join(' ')}'"].join("\n")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
tpl + shw
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.generate_zsh
|
|
15
|
+
gem_root = File.join File.dirname(__FILE__), '../../'
|
|
16
|
+
tpl = File.read File.join(gem_root, 'shell/completions/functions.zsh')
|
|
17
|
+
indent = 10
|
|
18
|
+
versions = Cani.api.browsers.reduce String.new do |acc, browser|
|
|
19
|
+
acc + (' ' * (indent - 2)) + browser.abbr + ")\n" +
|
|
20
|
+
(' ' * indent) + "_arguments -C \"1: :(#{browser.versions.join(' ')})\"\n" +
|
|
21
|
+
(' ' * indent) + ";;\n"
|
|
22
|
+
end.strip
|
|
23
|
+
|
|
24
|
+
tpl.gsub('{{names}}', Cani.api.browsers.map(&:abbr).join(' '))
|
|
25
|
+
.gsub '{{versions}}', versions
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.generate_bash
|
|
29
|
+
gem_root = File.join File.dirname(__FILE__), '../../'
|
|
30
|
+
tpl = File.read File.join(gem_root, 'shell/completions/functions.bash')
|
|
31
|
+
indent = 10
|
|
32
|
+
versions = Cani.api.browsers.reduce String.new do |acc, browser|
|
|
33
|
+
acc + (' ' * (indent - 2)) + '"' + browser.abbr + "\")\n" +
|
|
34
|
+
(' ' * indent) + "COMPREPLY=($(compgen -W \"#{browser.versions.join(' ')}\" ${COMP_WORDS[COMP_CWORD]}))\n" +
|
|
35
|
+
(' ' * indent) + ";;\n"
|
|
36
|
+
end.strip
|
|
37
|
+
|
|
38
|
+
tpl.gsub('{{names}}', Cani.api.browsers.map(&:abbr).join(' '))
|
|
39
|
+
.gsub '{{versions}}', versions
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.install!
|
|
43
|
+
# create all parent folders
|
|
44
|
+
FileUtils.mkdir_p Cani.api.config.fish_comp_dir
|
|
45
|
+
FileUtils.mkdir_p Cani.api.config.comp_dir
|
|
46
|
+
|
|
47
|
+
# write each completion file
|
|
48
|
+
File.open File.join(Cani.api.config.fish_comp_dir, 'cani.fish'), 'w' do |file|
|
|
49
|
+
file << generate_fish
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
%w[bash zsh].each do |shell|
|
|
53
|
+
File.open File.join(Cani.api.config.comp_dir, "_cani.#{shell}"), 'w' do |file|
|
|
54
|
+
file << send("generate_#{shell}")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# append source lines to configurations
|
|
59
|
+
insert_source_lines!
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.remove!
|
|
63
|
+
fish_comp = File.join Cani.api.config.fish_comp_dir, 'cani.fish'
|
|
64
|
+
|
|
65
|
+
File.unlink fish_comp if File.exist? fish_comp
|
|
66
|
+
|
|
67
|
+
%w[bash zsh].each do |shell|
|
|
68
|
+
shell_comp = File.join Cani.api.config.comp_dir, "_cani.#{shell}"
|
|
69
|
+
|
|
70
|
+
File.unlink shell_comp if File.exist? shell_comp
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# remove source lines to configurations
|
|
74
|
+
delete_source_lines!
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.delete_source_lines!
|
|
78
|
+
%w[bash zsh].each do |shell|
|
|
79
|
+
shellrc = File.join Dir.home, ".#{shell}rc"
|
|
80
|
+
lines = File.read(shellrc).split "\n"
|
|
81
|
+
comp_path = File.join Cani.api.config.comp_dir, "_cani.#{shell}"
|
|
82
|
+
rm_idx = lines.find_index { |l| l.match? comp_path }
|
|
83
|
+
|
|
84
|
+
lines.delete_at rm_idx unless rm_idx.nil?
|
|
85
|
+
File.write shellrc, lines.join("\n")
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def self.insert_source_lines!
|
|
90
|
+
%w[bash zsh].each do |shell|
|
|
91
|
+
shellrc = File.join Dir.home, ".#{shell}rc"
|
|
92
|
+
lines = File.read(shellrc).split "\n"
|
|
93
|
+
comp_path = File.join Cani.api.config.comp_dir, "_cani.#{shell}"
|
|
94
|
+
slidx = lines.find_index { |l| l.match? comp_path }
|
|
95
|
+
|
|
96
|
+
if slidx
|
|
97
|
+
lines[slidx] =
|
|
98
|
+
"#{lines[slidx][/^\s+/]}[ -f #{comp_path} ] && source #{comp_path}"
|
|
99
|
+
else
|
|
100
|
+
lines << "[ -f #{comp_path} ] && source #{comp_path}"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
File.write shellrc, lines.join("\n")
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
data/lib/cani/fzf.rb
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module Cani
|
|
2
|
+
module Fzf
|
|
3
|
+
def self.pick(rows, **opts)
|
|
4
|
+
unless executable?
|
|
5
|
+
puts 'fatal: command "fzf" not found, is it installed?'
|
|
6
|
+
exit 1
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
if STDOUT.tty?
|
|
10
|
+
rows = tableize_rows(rows, **opts).join "\n"
|
|
11
|
+
ohdr = opts.fetch :header, []
|
|
12
|
+
header = ohdr.is_a?(Array) ? [:cani, *ohdr].map { |v| v.to_s.downcase }.join(':')
|
|
13
|
+
: 'cani:' + ohdr.to_s
|
|
14
|
+
|
|
15
|
+
`echo "#{rows}" | fzf --ansi --header="[#{header}]"`.split ' '
|
|
16
|
+
else
|
|
17
|
+
# when output of any initial command is being piped
|
|
18
|
+
# print results and exit this command.
|
|
19
|
+
puts tableize_rows(rows).join "\n"
|
|
20
|
+
exit
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.executable?
|
|
25
|
+
@exe ||= begin
|
|
26
|
+
`command -v fzf`
|
|
27
|
+
$?.success?
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.feature_rows
|
|
32
|
+
Cani.api.features.map do |ft|
|
|
33
|
+
pc = format('%.2f%%', ft.percent).rjust 6
|
|
34
|
+
tt = format('%-24s', ft.title.size > 24 ? ft.title[0..23].strip + '..'
|
|
35
|
+
: ft.title)
|
|
36
|
+
|
|
37
|
+
["[#{ft.status}]", pc, tt, *ft.current_support]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.browser_rows
|
|
42
|
+
Cani.api.browsers.map do |bwsr|
|
|
43
|
+
[bwsr.title, 'usage: ' + format('%.4f%%', bwsr.usage.values.sum)]
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.browser_usage_rows(brwsr)
|
|
48
|
+
brwsr.usage.map { |(v, u)| [v, 'usage: ' + format('%.4f%%', u)] }.reverse
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.browser_feature_rows(brwsr, version)
|
|
52
|
+
features_by_support = brwsr.features_for version
|
|
53
|
+
|
|
54
|
+
Api::Feature::TYPES.flat_map do |(status, type)|
|
|
55
|
+
if (features = features_by_support.fetch(type[:name], nil))
|
|
56
|
+
features.map do |feature|
|
|
57
|
+
["[#{feature[:status]}]", "[#{type[:symbol]}]", feature[:title]]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end.compact
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.tableize_rows(rows, **opts)
|
|
64
|
+
col_widths = []
|
|
65
|
+
colors = opts.fetch :colors, []
|
|
66
|
+
|
|
67
|
+
rows.each do |row|
|
|
68
|
+
row.each.with_index do |column, i|
|
|
69
|
+
col_width = column.size
|
|
70
|
+
col_widths[i] = col_width if col_width > col_widths[i].to_i
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
rows.map do |row|
|
|
75
|
+
row.map.with_index do |col, i|
|
|
76
|
+
result = col.to_s.ljust col_widths[i]
|
|
77
|
+
|
|
78
|
+
if STDOUT.tty?
|
|
79
|
+
result.colorize(colors[i] || colors[-1] || :default)
|
|
80
|
+
.gsub '"', '\"'
|
|
81
|
+
else
|
|
82
|
+
result
|
|
83
|
+
end
|
|
84
|
+
end.join(' ').rstrip
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
data/lib/cani/version.rb
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# This is a template file that adds the most basic top level completions.
|
|
2
|
+
# It is used during installation of completions via the CLI.
|
|
3
|
+
# Unlike the functions.fish file, this one cannot be run directly.
|
|
4
|
+
#
|
|
5
|
+
# The invalid syntax will be replaced with valid BASH code
|
|
6
|
+
# during the installation process.
|
|
7
|
+
|
|
8
|
+
_cani_completions() {
|
|
9
|
+
case "${COMP_WORDS[1]}" in
|
|
10
|
+
"show")
|
|
11
|
+
case "${COMP_WORDS[2]}" in
|
|
12
|
+
{{versions}}
|
|
13
|
+
*)
|
|
14
|
+
COMPREPLY=($(compgen -W "{{names}}" "${COMP_WORDS[COMP_CWORD]}"))
|
|
15
|
+
;;
|
|
16
|
+
esac
|
|
17
|
+
;;
|
|
18
|
+
*)
|
|
19
|
+
COMPREPLY=($(compgen -W "use show help version" "${COMP_WORDS[COMP_CWORD]}"))
|
|
20
|
+
;;
|
|
21
|
+
esac
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
complete -F _cani_completions cani
|
|
25
|
+
|
|
26
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# This is a template file that adds the most basic top level completions.
|
|
2
|
+
# It is used during installation of completions via the CLI.
|
|
3
|
+
#
|
|
4
|
+
# Extra completions are added in the installation process.
|
|
5
|
+
# These include completions for browsers and versions.
|
|
6
|
+
|
|
7
|
+
function __fish_cani_needs_command
|
|
8
|
+
set -l cmd (commandline -opc)
|
|
9
|
+
set -q cmd[2]
|
|
10
|
+
or return 0
|
|
11
|
+
|
|
12
|
+
echo $cmd[2..-1]
|
|
13
|
+
return 1
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
function __fish_cani_showing_browser
|
|
17
|
+
set -l cmd (__fish_cani_needs_command | string split ' ')
|
|
18
|
+
|
|
19
|
+
test "$cmd[1]" = "show"
|
|
20
|
+
and contains -- "$cmd[2]" $argv
|
|
21
|
+
and return 0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
function __fish_cani_using_command
|
|
25
|
+
set -l cmd (__fish_cani_needs_command)
|
|
26
|
+
|
|
27
|
+
test -z "$cmd"
|
|
28
|
+
and return 1
|
|
29
|
+
|
|
30
|
+
contains -- $cmd $argv
|
|
31
|
+
and return 0
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
complete -f -c cani
|
|
35
|
+
|
|
36
|
+
complete -f -c cani -n '__fish_cani_needs_command' -a 'use' -d 'Display an overview of features including support'
|
|
37
|
+
complete -f -c cani -n '__fish_cani_needs_command' -a 'show' -d 'Display feature support for a specific browser'
|
|
38
|
+
complete -f -c cani -n '__fish_cani_needs_command' -a 'help' -d 'Show command help'
|
|
39
|
+
complete -f -c cani -n '__fish_cani_needs_command' -a 'version' -d 'Print the version number'
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# This is a template file that adds the most basic top level completions.
|
|
2
|
+
# It is used during installation of completions via the CLI.
|
|
3
|
+
# Unlike the functions.fish file, this one cannot be run directly.
|
|
4
|
+
#
|
|
5
|
+
# The invalid syntax will be replaced with valid ZSH code
|
|
6
|
+
# during the installation process.
|
|
7
|
+
|
|
8
|
+
function _cani {
|
|
9
|
+
local line
|
|
10
|
+
|
|
11
|
+
_arguments -C "1: :(use show help version)" \
|
|
12
|
+
"*::arg:->args"
|
|
13
|
+
|
|
14
|
+
case $line[1] in
|
|
15
|
+
show)
|
|
16
|
+
_arguments -C "1: :({{names}})" \
|
|
17
|
+
"*::arg:->args"
|
|
18
|
+
|
|
19
|
+
case $line[1] in
|
|
20
|
+
{{versions}}
|
|
21
|
+
esac
|
|
22
|
+
;;
|
|
23
|
+
esac
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
compdef _cani cani
|
metadata
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: cani
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Sidney Liebrand
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-07-08 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: colorize
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: json
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: bundler
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '1.16'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '1.16'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '10.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '10.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: pry
|
|
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: rspec
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '3.0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '3.0'
|
|
97
|
+
description: 'A rework of the ruby script from my medium post: https://medium.com/@sidneyliebrand/combining-caniuse-with-fzf-fb93ad235bae'
|
|
98
|
+
email:
|
|
99
|
+
- sidneyliebrand@gmail.com
|
|
100
|
+
executables:
|
|
101
|
+
- cani
|
|
102
|
+
extensions: []
|
|
103
|
+
extra_rdoc_files: []
|
|
104
|
+
files:
|
|
105
|
+
- ".gitignore"
|
|
106
|
+
- ".rspec"
|
|
107
|
+
- ".travis.yml"
|
|
108
|
+
- CODE_OF_CONDUCT.md
|
|
109
|
+
- Gemfile
|
|
110
|
+
- Gemfile.lock
|
|
111
|
+
- LICENSE.txt
|
|
112
|
+
- README.md
|
|
113
|
+
- Rakefile
|
|
114
|
+
- bin/console
|
|
115
|
+
- bin/setup
|
|
116
|
+
- cani.gemspec
|
|
117
|
+
- exe/cani
|
|
118
|
+
- lib/cani.rb
|
|
119
|
+
- lib/cani/api.rb
|
|
120
|
+
- lib/cani/api/browser.rb
|
|
121
|
+
- lib/cani/api/config.rb
|
|
122
|
+
- lib/cani/api/feature.rb
|
|
123
|
+
- lib/cani/completions.rb
|
|
124
|
+
- lib/cani/fzf.rb
|
|
125
|
+
- lib/cani/version.rb
|
|
126
|
+
- shell/completions/functions.bash
|
|
127
|
+
- shell/completions/functions.fish
|
|
128
|
+
- shell/completions/functions.zsh
|
|
129
|
+
homepage: https://github.com/SidOfc/cani
|
|
130
|
+
licenses:
|
|
131
|
+
- MIT
|
|
132
|
+
metadata: {}
|
|
133
|
+
post_install_message:
|
|
134
|
+
rdoc_options: []
|
|
135
|
+
require_paths:
|
|
136
|
+
- lib
|
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
|
+
requirements:
|
|
139
|
+
- - ">="
|
|
140
|
+
- !ruby/object:Gem::Version
|
|
141
|
+
version: '0'
|
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
|
+
requirements:
|
|
144
|
+
- - ">="
|
|
145
|
+
- !ruby/object:Gem::Version
|
|
146
|
+
version: '0'
|
|
147
|
+
requirements: []
|
|
148
|
+
rubyforge_project:
|
|
149
|
+
rubygems_version: 2.7.6
|
|
150
|
+
signing_key:
|
|
151
|
+
specification_version: 4
|
|
152
|
+
summary: A simple caniuse CLI.
|
|
153
|
+
test_files: []
|