snippit 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9e6f255093428dc4ac4db2ce12c14e9d78e45f1cdfb07eeccb19ea7736bef6e
4
- data.tar.gz: a764c0e2abaffd53c42bd1556cc9ce76bf9fd2ce7b596299eb61fe0fadbe297d
3
+ metadata.gz: 5775f3ab82f74a1c2c4e713c05019b3143806abcb03f19edcff00190febd6a80
4
+ data.tar.gz: 66506019f73d7a499a56da84ffee296be03b60c7e41eb9bce59cc9eafe48c888
5
5
  SHA512:
6
- metadata.gz: 72f6978a479de66cbe424658b48fce6f0f85b8015c58b193b5d967b9eca4966b10d5ecf0287e67007485db551c202b7d15096a9311fe2c5ece7ebe67b25fc51e
7
- data.tar.gz: 03b95c849abf6b52e9f09b8681531b47b3fc9d5a8dd8015ba40293835d4c8144afd09aff57959499efafd6cc55b3629898df60bcbb5675ccefebefd71eb293c9
6
+ metadata.gz: b7375cc306156e3fd6d9597227355750c9567a62f2b9e75eb61a4110ade50b6767a5f4d335fa8da62f1c9765a7288ee4e4fbd413ee576427ef4a69e59bcbc04d
7
+ data.tar.gz: 561467cabc034d3dde1a223b90f6f9af30dd041a139018a0b0b9149985426295f28d0ad1eaa2bbb289bdeae8e0f36e8e14bf10f6ab953ca8a8b2e3c1259d140e
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- snippit (0.0.1)
4
+ snippit (1.0.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -13,16 +13,16 @@ GEM
13
13
  docile (1.4.0)
14
14
  iniparse (1.5.0)
15
15
  json (2.6.3)
16
- overcommit (0.59.1)
16
+ overcommit (0.60.0)
17
17
  childprocess (>= 0.6.3, < 5)
18
18
  iniparse (~> 1.4)
19
19
  rexml (~> 3.2)
20
20
  parallel (1.22.1)
21
- parser (3.1.3.0)
21
+ parser (3.2.0.0)
22
22
  ast (~> 2.4.1)
23
23
  rainbow (3.1.1)
24
24
  rake (13.0.6)
25
- regexp_parser (2.6.1)
25
+ regexp_parser (2.6.2)
26
26
  rexml (3.2.5)
27
27
  rspec (3.12.0)
28
28
  rspec-core (~> 3.12.0)
@@ -30,34 +30,37 @@ GEM
30
30
  rspec-mocks (~> 3.12.0)
31
31
  rspec-core (3.12.0)
32
32
  rspec-support (~> 3.12.0)
33
- rspec-expectations (3.12.0)
33
+ rspec-expectations (3.12.2)
34
34
  diff-lcs (>= 1.2.0, < 2.0)
35
35
  rspec-support (~> 3.12.0)
36
- rspec-mocks (3.12.0)
36
+ rspec-mocks (3.12.3)
37
37
  diff-lcs (>= 1.2.0, < 2.0)
38
38
  rspec-support (~> 3.12.0)
39
39
  rspec-snapshot (2.0.1)
40
40
  awesome_print (> 1.0.0)
41
41
  rspec (> 3.0.0)
42
42
  rspec-support (3.12.0)
43
- rubocop (1.40.0)
43
+ rubocop (1.44.1)
44
44
  json (~> 2.3)
45
45
  parallel (~> 1.10)
46
- parser (>= 3.1.2.1)
46
+ parser (>= 3.2.0.0)
47
47
  rainbow (>= 2.2.2, < 4.0)
48
48
  regexp_parser (>= 1.8, < 3.0)
49
49
  rexml (>= 3.2.5, < 4.0)
50
- rubocop-ast (>= 1.23.0, < 2.0)
50
+ rubocop-ast (>= 1.24.1, < 2.0)
51
51
  ruby-progressbar (~> 1.7)
52
- unicode-display_width (>= 1.4.0, < 3.0)
53
- rubocop-ast (1.24.0)
52
+ unicode-display_width (>= 2.4.0, < 3.0)
53
+ rubocop-ast (1.24.1)
54
54
  parser (>= 3.1.1.0)
55
+ rubocop-capybara (2.17.0)
56
+ rubocop (~> 1.41)
55
57
  rubocop-rake (0.6.0)
56
58
  rubocop (~> 1.0)
57
- rubocop-rspec (2.15.0)
59
+ rubocop-rspec (2.18.1)
58
60
  rubocop (~> 1.33)
61
+ rubocop-capybara (~> 2.17)
59
62
  ruby-progressbar (1.11.0)
60
- simplecov (0.21.2)
63
+ simplecov (0.22.0)
61
64
  docile (~> 1.1)
62
65
  simplecov-html (~> 0.11)
63
66
  simplecov_json_formatter (~> 0.1)
@@ -66,13 +69,13 @@ GEM
66
69
  simplecov (~> 0.19)
67
70
  simplecov-html (0.12.3)
68
71
  simplecov_json_formatter (0.1.4)
69
- unicode-display_width (2.3.0)
72
+ unicode-display_width (2.4.2)
70
73
  webrick (1.7.0)
71
74
  yard (0.9.28)
72
75
  webrick (~> 1.7.0)
73
76
 
74
77
  PLATFORMS
75
- ruby
78
+ x86_64-linux
76
79
 
77
80
  DEPENDENCIES
78
81
  bundler (~> 2.0)
@@ -89,4 +92,4 @@ DEPENDENCIES
89
92
  yard (~> 0.9.28)
90
93
 
91
94
  BUNDLED WITH
92
- 2.1.2
95
+ 2.4.5
data/README.md CHANGED
@@ -1,25 +1,33 @@
1
- # Snippit (WIP)
1
+ # Snippit
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/snippit.svg)](https://badge.fury.io/rb/snippit)
3
4
  [![CI](https://github.com/spenserblack/snippit/actions/workflows/ci.yml/badge.svg)](https://github.com/spenserblack/snippit/actions/workflows/ci.yml)
4
- [![CodeQL](https://github.com/spenserblack/snippit/actions/workflows/codeql.yml/badge.svg)](https://github.com/spenserblack/snippit/actions/workflows/codeql.yml)
5
+ [![CodeQL](https://github.com/spenserblack/snippit/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/spenserblack/snippit/actions/workflows/github-code-scanning/codeql)
5
6
  [![codecov](https://codecov.io/gh/spenserblack/snippit/branch/main/graph/badge.svg?token=5yr1302Knn)](https://codecov.io/gh/spenserblack/snippit)
6
7
 
7
8
  Define, store, and output your code snippets.
8
9
 
9
- ## (Proposed) Usage
10
+ ## Usage
10
11
 
11
12
  All uses of `snippit` can be replaced with the shortcut `snip`.
12
13
 
13
14
  ```console
14
15
  # Define a snippet called "hello-world.js"
15
- $ snippit save hello-world.js
16
+ $ snippit --save hello-world.js
16
17
 
17
- # Make a code snippet called "Hello World (JS)" from STDIN
18
- $ echo "console.log('Hello, World!');" | snippit save --name "Hello World (JS)"
18
+ # Set the slug and descriptive name of the snippet
19
+ $ snippit --save hello-world.js --slug hw-js --name "JavaScript Starter"
19
20
 
20
- # Output the "Hello World (JS)" snippet to a file called "index.js"
21
- $ snippit out "Hello World (JS)" > index.js
21
+ # Output the "JavaScript Starter" snippet to a file called "index.js"
22
+ $ snippit --get hw-js > index.js
22
23
 
23
24
  # List all of your code snippets
24
- $ snippit list
25
+ $ snippit --list
25
26
  ```
27
+
28
+ ## How it works
29
+
30
+ Code snippets get stored in a directory called `.snippit` in your home
31
+ directory. The actual code snippet filenames are slugified versions of the
32
+ snippet name. `.__definitions__.yml` is a reserved filename, as it is used to
33
+ map snippet slugs (filenames) to their human-readable names.
data/RELEASE_NOTES ADDED
@@ -0,0 +1,13 @@
1
+ Initial Version
2
+
3
+ ## Added
4
+
5
+ - `snippit --save` to save snippets
6
+ - `--add` alias
7
+ - `--force` to force overwriting
8
+ - `--name` to set a different name
9
+ - `--slug` to use a different slug (ID)
10
+ - `snippit --get` to get snippets
11
+ - `snippit --delete` to delete snippets
12
+ - `snippit --list` to list available snippets
13
+ - `snip` as an alias of `snippit`
data/exe/snip CHANGED
@@ -3,4 +3,4 @@
3
3
 
4
4
  require 'snippit/cli'
5
5
 
6
- Snippit::CLI.start(ARGV)
6
+ exit Snippit::CLI.new(ARGV).start
data/exe/snippit CHANGED
@@ -3,4 +3,4 @@
3
3
 
4
4
  require 'snippit/cli'
5
5
 
6
- Snippit::CLI.start(ARGV)
6
+ exit Snippit::CLI.new(ARGV).start
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'snippit/io'
4
+
5
+ module Snippit
6
+ class CLI
7
+ # Snippit::CLI::Delete deletes a snippet.
8
+ class Delete
9
+ include Snippit::IO
10
+
11
+ def initialize(slug)
12
+ @slug = slug
13
+ end
14
+
15
+ # Starts the delete subcommand.
16
+ #
17
+ # @return [Integer] the exit code
18
+ def start
19
+ return 1 unless definitions.key?(@slug)
20
+
21
+ delete_snippet(@slug)
22
+ 0
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'snippit/io'
4
+
5
+ module Snippit
6
+ class CLI
7
+ # Snippit::CLI::Get gets a snippet's contents.
8
+ class Get
9
+ include Snippit::IO
10
+
11
+ def initialize(slug)
12
+ @slug = slug
13
+ end
14
+
15
+ # Starts the get subcommand.
16
+ #
17
+ # @return [Integer] the exit code
18
+ def start
19
+ unless definitions.key?(@slug)
20
+ warn "Snippet '#{@slug}' does not exist."
21
+ return 1
22
+ end
23
+
24
+ print read_snippet(@slug)
25
+ 0
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'snippit/io'
4
+
5
+ module Snippit
6
+ class CLI
7
+ # Snippit::CLI::List lists all snippets.
8
+ class List
9
+ include Snippit::IO
10
+
11
+ # Starts the list subcommand.
12
+ #
13
+ # @return [Integer] the exit code
14
+ def start
15
+ out = definitions.map { |slug, name| "#{slug}: #{name}" }
16
+
17
+ puts 'No snippets found.' if out.empty?
18
+
19
+ puts out
20
+ 0
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'snippit/exceptions'
4
+ require 'snippit/io'
5
+ require 'snippit/slugify'
6
+
7
+ module Snippit
8
+ class CLI
9
+ # Snippit::CLI::Save saves a new snippet, or overwrites an existing one.
10
+ class Save
11
+ include Snippit::IO
12
+ include Snippit::Slugify
13
+
14
+ # Initializes a new Save subcommand.
15
+ #
16
+ # @param [String] path The path to the snippet to save
17
+ # @param [TrueClass|FalseClass] force
18
+ def initialize(path, force: false, name: nil, slug: nil)
19
+ @force = force
20
+ @path = path
21
+ @name = name
22
+ @slug = slug
23
+ end
24
+
25
+ # Starts the save subcommand.
26
+ #
27
+ # @return [Integer] the exit code
28
+ def start
29
+ base_name = File.basename(@path)
30
+ slug = @slug || slugify(base_name)
31
+
32
+ handle(slug) { write_snippet(@name || base_name, slug, File.read(@path), @force) }
33
+ end
34
+
35
+ private
36
+
37
+ # Executes a provided block, handling errors.
38
+ def handle(slug)
39
+ begin
40
+ yield
41
+ rescue Snippit::SnippetExistsError
42
+ warn "Snippet #{slug} already exists. Use --force to overwrite."
43
+ return 1
44
+ rescue Snippit::ReservedFilenameError
45
+ warn "#{slug} is a reserved filename. Please choose another."
46
+ return 1
47
+ end
48
+
49
+ 0
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'snippit/version'
4
+
5
+ module Snippit
6
+ class CLI
7
+ # Snippit::CLI::Version is the version subcommand for Snippit::CLI.
8
+ class Version
9
+ # Starts the version subcommand.
10
+ #
11
+ # @return [Integer] the exit code
12
+ def start
13
+ puts "snippit #{Snippit::VERSION}"
14
+ 0
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/snippit/cli.rb CHANGED
@@ -1,20 +1,106 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'snippit'
3
+ require 'optparse'
4
+ require 'snippit/cli/delete'
5
+ require 'snippit/cli/get'
6
+ require 'snippit/cli/list'
7
+ require 'snippit/cli/save'
8
+ require 'snippit/cli/version'
4
9
 
5
10
  module Snippit
6
11
  # Snippit::CLI is the command line interface for Snippit.
7
12
  class CLI
8
- def self.start(args)
9
- new(args).run
10
- end
11
-
12
13
  def initialize(args)
13
14
  @args = args
15
+ @opts = {}
16
+ end
17
+
18
+ # Starts the CLI.
19
+ #
20
+ # @return [Integer] the exit code
21
+ def start
22
+ parser.parse!(@args)
23
+
24
+ return Version.new.start if @opts[:version]
25
+
26
+ result = start_io
27
+ return result unless result.nil?
28
+
29
+ bad_usage
30
+ end
31
+
32
+ private
33
+
34
+ def parser
35
+ OptionParser.new do |opts|
36
+ opts.banner = 'Usage: snippit|snip [options]'
37
+ opts.separator ''
38
+ opts.separator 'Options:'
39
+ add_list_option(opts)
40
+ add_save_option(opts)
41
+ add_get_option(opts)
42
+ add_delete_option(opts)
43
+ add_version_option(opts)
44
+ end
45
+ end
46
+
47
+ def add_save_option(opts)
48
+ opts.on('-s', '-a', '--save PATH', '--add PATH', 'Save a new snippet') do |path|
49
+ @opts[:save] = path
50
+ end
51
+ add_save_helper_options(opts)
52
+ end
53
+
54
+ def add_save_helper_options(opts)
55
+ opts.on('-f', '--force', 'Used with --save to overwrite existing snippets') do
56
+ @opts[:force] = true
57
+ end
58
+ opts.on('-n', '--name NAME', 'Used with --save to set the snippet name') do |name|
59
+ @opts[:name] = name
60
+ end
61
+ opts.on('-S', '--slug SLUG', 'Used with --save to set the snippet slug') do |slug|
62
+ @opts[:slug] = slug
63
+ end
14
64
  end
15
65
 
16
- def run
17
- puts 'Hello, world!'
66
+ def add_get_option(opts)
67
+ opts.on('-g', '--get SLUG', 'Get a snippet') do |slug|
68
+ @opts[:get] = slug
69
+ end
70
+ end
71
+
72
+ def add_delete_option(opts)
73
+ opts.on('-d', '--delete SLUG', 'Delete a snippet') do |slug|
74
+ @opts[:delete] = slug
75
+ end
76
+ end
77
+
78
+ def add_list_option(opts)
79
+ opts.on('-l', '--list', 'List all snippet slugs and their names') do
80
+ @opts[:list] = true
81
+ end
82
+ end
83
+
84
+ def add_version_option(opts)
85
+ opts.on('-v', '--version', 'Print version') do
86
+ @opts[:version] = true
87
+ end
88
+ end
89
+
90
+ def bad_usage
91
+ puts parser
92
+ 1
93
+ end
94
+
95
+ # Handles options that require IO.
96
+ def start_io
97
+ return List.new.start if @opts[:list]
98
+
99
+ return Save.new(@opts[:save], **@opts.slice(:force, :name, :slug)).start if @opts.key?(:save)
100
+
101
+ return Get.new(@opts[:get]).start if @opts.key?(:get)
102
+
103
+ return Delete.new(@opts[:delete]).start if @opts.key?(:delete)
18
104
  end
19
105
  end
20
106
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Snippit
4
+ class Error < RuntimeError
5
+ end
6
+
7
+ class SnippetExistsError < Error
8
+ end
9
+
10
+ class SnippetNotFoundError < Error
11
+ end
12
+
13
+ class ReservedFilenameError < Error
14
+ end
15
+ end
data/lib/snippit/io.rb ADDED
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'snippit/exceptions'
4
+ require 'yaml'
5
+
6
+ module Snippit
7
+ # Provides IO functions for Snippit.
8
+ module IO
9
+ # Reads teh contents of a code snippet.
10
+ #
11
+ # @param [String] slug The name of the snippet.
12
+ # @return [String] The contents of the snippet.
13
+ def read_snippet(slug)
14
+ raise SnippetNotFoundError, slug unless File.exist?(filepath!(slug))
15
+
16
+ File.read(filepath!(slug))
17
+ end
18
+
19
+ # Writes the contents of a code snippet to a file.
20
+ #
21
+ # @param [String] name The name of the snippet
22
+ # @param [String] slug The name of the file. Relative to the ~/.snippit directory
23
+ # @param [String] contents The contents of the file.
24
+ # @param [TrueClass|FalseClass] force Whether or not to overwrite an existing file.
25
+ def write_snippet(name, slug, contents, force)
26
+ raise ReservedFilenameError, slug if slug == '.__definitions__.yml'
27
+
28
+ raise SnippetExistsError, slug if File.exist?(filepath!(slug)) && !force
29
+
30
+ File.write(filepath!(slug), contents)
31
+ write_definition(definitions.merge(slug => name))
32
+ end
33
+
34
+ # Removes a code snippet.
35
+ #
36
+ # @param [String] slug The name of the file. Relative to the ~/.snippit directory
37
+ def delete_snippet(slug)
38
+ raise ReservedFilenameError, slug if slug == '.__definitions__.yml'
39
+
40
+ File.delete(filepath!(slug))
41
+ write_definition(definitions.reject { |k, _v| k == slug })
42
+ end
43
+
44
+ # Gets the filepath to the snippet.
45
+ #
46
+ # Also creates the ~/.snippit directory if it does not exist.
47
+ #
48
+ # @param [String] slug The name of the snippet.
49
+ def filepath!(slug)
50
+ File.expand_path(slug, snippit_dir!)
51
+ end
52
+
53
+ # Creates the ~/.snippit directory if it does not exist, and returns the path to it.
54
+ def snippit_dir!
55
+ FileUtils.mkdir_p(snippit_dir)
56
+ snippit_dir
57
+ end
58
+
59
+ # Returns the path to the ~/.snippit directory.
60
+ def snippit_dir
61
+ File.expand_path('.snippit', Dir.home)
62
+ end
63
+
64
+ # Loads the ~/.snippit/.__definitions__.yml file if it exists, or returns an empty hash.
65
+ def definitions
66
+ return {} unless File.exist?(definitions_file)
67
+
68
+ @definitions ||= YAML.load_file(definitions_file)
69
+ end
70
+
71
+ # Writes the definitions to the ~/.snippit/.__definitions__.yml file.
72
+ def write_definition(definitions)
73
+ File.write(definitions_file, definitions.to_yaml)
74
+ end
75
+
76
+ # Returns the path to the ~/.snippit/.__definitions__.yml file.
77
+ def definitions_file
78
+ File.expand_path('.__definitions__.yml', snippit_dir)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Snippit
4
+ # Provides the slugify function
5
+ module Slugify
6
+ # Slugify a string
7
+ #
8
+ # @param [String] str The string to slugify
9
+ # @return [String] The slugified string
10
+ def slugify(str)
11
+ str.strip.downcase.gsub(/ /, '-').gsub(/[^a-z0-9\-_.]/, '')
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Snippit
4
+ VERSION = File.read(File.expand_path('VERSION', __dir__)).strip
5
+ end
data/lib/snippit.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'snippit/version'
4
+
3
5
  # Snippit manages code snippets.
4
- module Snippit
6
+ class Snippit
5
7
  # Snippit::DEFINITIONS is a reserved filename for the snippet definitions.
6
8
  DEFINITIONS = '__definitions__.yml'
7
9
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snippit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Spenser Black
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-13 00:00:00.000000000 Z
11
+ date: 2023-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -175,11 +175,22 @@ files:
175
175
  - Gemfile
176
176
  - Gemfile.lock
177
177
  - README.md
178
+ - RELEASE_NOTES
178
179
  - Rakefile
179
180
  - exe/snip
180
181
  - exe/snippit
181
182
  - lib/snippit.rb
183
+ - lib/snippit/VERSION
182
184
  - lib/snippit/cli.rb
185
+ - lib/snippit/cli/delete.rb
186
+ - lib/snippit/cli/get.rb
187
+ - lib/snippit/cli/list.rb
188
+ - lib/snippit/cli/save.rb
189
+ - lib/snippit/cli/version.rb
190
+ - lib/snippit/exceptions.rb
191
+ - lib/snippit/io.rb
192
+ - lib/snippit/slugify.rb
193
+ - lib/snippit/version.rb
183
194
  homepage: https://github.com/spenserblack/snippit
184
195
  licenses:
185
196
  - MIT
@@ -205,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
205
216
  - !ruby/object:Gem::Version
206
217
  version: '0'
207
218
  requirements: []
208
- rubygems_version: 3.1.2
219
+ rubygems_version: 3.1.6
209
220
  signing_key:
210
221
  specification_version: 4
211
222
  summary: Manage your personal code snippets