code_snippet 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,52 @@
1
+ require 'code_snippet/cli/commands'
2
+ require 'code_snippet/cli/presenters'
3
+
4
+ require 'logger'
5
+
6
+ module CodeSnippet
7
+ # Command line interface helpers and actions
8
+ module CLI
9
+ # CLI Helpers
10
+ class <<self
11
+ ##
12
+ # Retrieves snippet dir from environment
13
+ #
14
+ def snip_dir
15
+ @snippet_dir = ENV['SNIPPET_DIR']
16
+ raise 'SNIPPET_DIR environment variable not set' unless @snippet_dir
17
+ unless File.exist?(@snippet_dir)
18
+ raise "SNIPPET_DIR #{@snippet_dir} does not exist"
19
+ end
20
+
21
+ @snippet_dir
22
+ end
23
+
24
+ ##
25
+ # Creates logger for printing messages
26
+ #
27
+ def logger
28
+ @logger ||= Logger.new(STDOUT)
29
+ @logger.formatter = proc do |_sev, _time, _prog, msg|
30
+ "#{msg}\n"
31
+ end
32
+
33
+ @logger
34
+ end
35
+
36
+ ##
37
+ # Prints command line message to CLI
38
+ #
39
+ def print_message(message)
40
+ logger.info(message)
41
+ end
42
+
43
+ ##
44
+ # Prints a message and then exits with given status code
45
+ #
46
+ def print_message_and_exit(message, exit_code = 1)
47
+ print_message(message)
48
+ exit(exit_code)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,50 @@
1
+ module CodeSnippet
2
+ # Manager looks after a set of snippets
3
+ class Manager
4
+ DEFAULT_QUERY = ->(_snip) { return true }
5
+
6
+ attr_reader :snippets
7
+
8
+ def initialize(snippet_dir)
9
+ @snippet_dir = snippet_dir
10
+ @snippets = []
11
+ end
12
+
13
+ def load_snippets
14
+ Dir.glob(File.join(@snippet_dir, '**', '*')).each do |file|
15
+ next if File.directory?(file)
16
+
17
+ @snippets << CodeSnippet::Snip.new_from_file(file)
18
+ end
19
+ end
20
+
21
+ def filter(query = DEFAULT_QUERY)
22
+ @snippets.select do |snip|
23
+ query.call(snip)
24
+ end
25
+ end
26
+
27
+ def find(search_term, lang = nil)
28
+ name_query = lambda do |snip|
29
+ snip.name.include?(search_term)
30
+ end
31
+
32
+ results = filter(name_query)
33
+
34
+ unless lang.nil?
35
+ results = results.select do |snip|
36
+ snip.ext == lang
37
+ end
38
+ end
39
+
40
+ results
41
+ end
42
+
43
+ def filter_by_extension(ext)
44
+ ext = ".#{ext}" unless ext.start_with?('.')
45
+
46
+ ext_query = ->(snip) { snip.ext == ext }
47
+ filter(ext_query)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,35 @@
1
+ module CodeSnippet
2
+ # Snip is a file with a code snippet
3
+ class Snip
4
+ attr_reader :path, :ext
5
+
6
+ def self.new_from_file(path)
7
+ new(
8
+ path,
9
+ File.basename(path),
10
+ File.extname(path)
11
+ )
12
+ end
13
+
14
+ def initialize(path, name, ext)
15
+ @path = path
16
+ @name = name
17
+ @ext = ext
18
+ end
19
+
20
+ def name
21
+ @name
22
+ .gsub(@ext, '')
23
+ end
24
+
25
+ def exist?
26
+ File.exist?(@path)
27
+ end
28
+
29
+ def content
30
+ raise 'cannot read snippet code' unless exist?
31
+
32
+ File.read(@path)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module CodeSnippet
2
+ VERSION = '0.2.6'.freeze
3
+ end
@@ -0,0 +1,7 @@
1
+ require 'code_snippet/version'
2
+ require 'code_snippet/manager'
3
+ require 'code_snippet/snip'
4
+
5
+ # Snippet is the namespace for snippet
6
+ module CodeSnippet
7
+ end
@@ -0,0 +1,104 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CodeSnippet::CLI::Commands do
4
+ let(:snippet_dir) { 'path/to/snippets' }
5
+ let(:lang) { '.rb' }
6
+ let(:copy) { false }
7
+ let(:vars) { [] }
8
+
9
+ before do
10
+ allow(ENV)
11
+ .to receive(:[]).with('SNIPPET_DIR')
12
+ .and_return(snippet_dir)
13
+
14
+ allow(File)
15
+ .to receive(:exist?)
16
+ .with(snippet_dir)
17
+ .and_return(true)
18
+ end
19
+
20
+ describe '.show' do
21
+ end
22
+
23
+ describe '.list' do
24
+ let(:manager) { double(CodeSnippet::Manager) }
25
+ let(:lang) { nil }
26
+
27
+ let(:snip) { double(CodeSnippet::Snip) }
28
+ let(:filtered_snips) { [snip, snip] }
29
+ let(:all_snips) { [snip, snip, snip, snip] }
30
+
31
+ before do
32
+ allow(CodeSnippet::CLI).to receive(:snip_dir)
33
+ .and_return('path/to/snips')
34
+
35
+ allow(CodeSnippet::Manager)
36
+ .to receive(:new)
37
+ .and_return(manager)
38
+
39
+ allow(manager).to receive(:load_snippets)
40
+ allow(manager)
41
+ .to receive(:filter_by_extension)
42
+ .and_return(filtered_snips)
43
+
44
+ allow(manager)
45
+ .to receive(:snippets)
46
+ .and_return(all_snips)
47
+
48
+ allow(CodeSnippet::CLI::Presenters)
49
+ .to receive(:list_snippets)
50
+
51
+ described_class.list(lang, copy, vars)
52
+ end
53
+
54
+ it 'loads snippets' do
55
+ expect(manager).to have_received(:load_snippets)
56
+ end
57
+
58
+ it 'presents a list of snippets' do
59
+ expect(CodeSnippet::CLI::Presenters)
60
+ .to have_received(:list_snippets)
61
+ .with(all_snips)
62
+ end
63
+
64
+ context 'when language filter is applied' do
65
+ let(:lang) { '.rb' }
66
+
67
+ it 'presents a list of filtered snippets' do
68
+ expect(CodeSnippet::CLI::Presenters)
69
+ .to have_received(:list_snippets)
70
+ .with(filtered_snips)
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '.path' do
76
+ before do
77
+ allow(CodeSnippet::CLI)
78
+ .to receive(:print_message)
79
+ .and_return(nil)
80
+
81
+ described_class.path(lang, copy, vars)
82
+ end
83
+
84
+ it 'prints snippet dir' do
85
+ expect(CodeSnippet::CLI).to have_received(:print_message)
86
+ .with(snippet_dir)
87
+ end
88
+ end
89
+
90
+ describe '.version' do
91
+ before do
92
+ allow(CodeSnippet::CLI)
93
+ .to receive(:print_message)
94
+ .and_return(nil)
95
+
96
+ described_class.version(lang, copy, vars)
97
+ end
98
+
99
+ it 'prints snippet version' do
100
+ expect(CodeSnippet::CLI).to have_received(:print_message)
101
+ .with(CodeSnippet::VERSION)
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CodeSnippet::CLI::Presenters do
4
+ let(:snippets) { [snip, snip] }
5
+
6
+ let(:snip) { double(CodeSnippet::Snip) }
7
+ let(:name) { 'snippet' }
8
+ let(:ext) { '.snip' }
9
+ let(:path) { 'path/to/snippet' }
10
+ let(:content) { 'snip-content' }
11
+
12
+ before do
13
+ allow(CodeSnippet::CLI).to receive(:print_message).and_return(double)
14
+ allow(snip).to receive(:name).and_return(name)
15
+ allow(snip).to receive(:ext).and_return(ext)
16
+ allow(snip).to receive(:path).and_return(path)
17
+ allow(snip).to receive(:content).and_return(content)
18
+ end
19
+ describe '.pick_from' do
20
+ let(:question) { 'are you sure?' }
21
+ let(:snips) { [snip, snip] }
22
+ let(:prompt) { double(TTY::Prompt) }
23
+
24
+ before do
25
+ allow(TTY::Prompt).to receive(:new).and_return(prompt)
26
+ allow(prompt).to receive(:select).and_return(path)
27
+ @choice = subject.pick_from(question, snips)
28
+ end
29
+
30
+ it 'asks which path' do
31
+ expect(prompt).to have_received(:select)
32
+ .with(question, [path, path])
33
+ end
34
+
35
+ it 'returns chosen path' do
36
+ expect(@choice).to eq snip
37
+ end
38
+ end
39
+
40
+ describe '.show' do
41
+ before do
42
+ described_class.show(snip)
43
+ end
44
+
45
+ it 'puts content to STDOUT' do
46
+ expect(CodeSnippet::CLI).to have_received(:print_message).with(content)
47
+ end
48
+ end
49
+
50
+ describe '.list_snippets' do
51
+ let(:table) { double(TTY::Table) }
52
+ before do
53
+ allow(TTY::Table).to receive(:new).and_return(table)
54
+ allow(table).to receive(:render)
55
+ described_class.list_snippets(snippets)
56
+ end
57
+
58
+ it 'sets up render' do
59
+ expect(TTY::Table).to have_received(:new).with(
60
+ %w[NAME LANG PATH],
61
+ [
62
+ [name, ext, path],
63
+ [name, ext, path]
64
+ ]
65
+ )
66
+ end
67
+
68
+ it 'renders table' do
69
+ expect(table).to have_received(:render).with(:ascii)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CodeSnippet::CLI do
4
+ let(:logger) { double(Logger) }
5
+ let(:message) { 'lorem ipsum' }
6
+
7
+ before do
8
+ allow(described_class).to receive(:exit).and_return(double)
9
+ end
10
+
11
+ describe '.snip_dir' do
12
+ let(:snippet_dir) { 'path/to/snippets' }
13
+ let(:exists) { true }
14
+
15
+ before do
16
+ allow(ENV)
17
+ .to receive(:[])
18
+ .with('SNIPPET_DIR')
19
+ .and_return(snippet_dir)
20
+
21
+ allow(File).to receive(:exist?).and_return(exists)
22
+ end
23
+
24
+ it 'returns path to snippets' do
25
+ expect(described_class.snip_dir).to eq snippet_dir
26
+ end
27
+
28
+ context 'when the environment variable is not set' do
29
+ let(:snippet_dir) { nil }
30
+
31
+ it 'raises error' do
32
+ expect do
33
+ described_class.snip_dir
34
+ end.to raise_error 'SNIPPET_DIR environment variable not set'
35
+ end
36
+ end
37
+
38
+ context 'when the snippet directory does not exist' do
39
+ let(:exists) { false }
40
+
41
+ it 'raises error' do
42
+ expect do
43
+ described_class.snip_dir
44
+ end.to raise_error "SNIPPET_DIR #{snippet_dir} does not exist"
45
+ end
46
+ end
47
+ end
48
+
49
+ describe '.logger' do
50
+ before do
51
+ allow(Logger).to receive(:new).and_return(logger)
52
+ allow(logger).to receive(:formatter=)
53
+ @logger = subject.logger
54
+ end
55
+
56
+ it 'creates a new logger to STDOUT' do
57
+ expect(Logger).to have_received(:new).with(STDOUT)
58
+ end
59
+ end
60
+
61
+ describe '.print_message' do
62
+ let(:logger) { double(Logger) }
63
+
64
+ before do
65
+ allow(described_class).to receive(:logger).and_return(logger)
66
+ allow(logger).to receive(:info)
67
+ described_class.print_message(message)
68
+ end
69
+
70
+ it 'prints message to stdout' do
71
+ expect(logger).to have_received(:info).with(message)
72
+ end
73
+ end
74
+
75
+ describe '.print_message_and_exit' do
76
+ let(:exit_code) { 2 }
77
+
78
+ before do
79
+ allow(described_class).to receive(:print_message)
80
+ described_class.print_message_and_exit(message, exit_code)
81
+ end
82
+
83
+ it 'prints message' do
84
+ expect(described_class).to have_received(:print_message).with(message)
85
+ end
86
+
87
+ it 'exits with set status' do
88
+ expect(described_class).to have_received(:exit).with(exit_code)
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,166 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CodeSnippet::Manager do
4
+ let(:snippet_dir) { 'path/to/snippets' }
5
+
6
+ subject do
7
+ described_class.new(snippet_dir)
8
+ end
9
+
10
+ describe '#load_snippets' do
11
+ let(:directory) { File.join(snippet_dir, 'path/to/dir') }
12
+ let(:file) { File.join(snippet_dir, 'path/to/dir/file') }
13
+
14
+ let(:file_snip) { double(CodeSnippet::Snip) }
15
+ let(:files) do
16
+ [
17
+ directory,
18
+ directory,
19
+ file,
20
+ file
21
+ ]
22
+ end
23
+
24
+ before do
25
+ allow(File).to receive(:directory?).with(directory)
26
+ .and_return(true)
27
+
28
+ allow(File).to receive(:directory?).with(file)
29
+ .and_return(false)
30
+
31
+ allow(Dir).to receive(:glob).and_return(files)
32
+ allow(CodeSnippet::Snip).to receive(:new_from_file)
33
+ .and_return(file_snip)
34
+
35
+ subject.load_snippets
36
+ end
37
+
38
+ it 'creates snips from files' do
39
+ expect(subject.snippets.count).to be 2
40
+
41
+ subject.snippets.each do |snip|
42
+ expect(snip).to eq file_snip
43
+ end
44
+ end
45
+ end
46
+
47
+ describe '#filter' do
48
+ let(:query) do
49
+ ->(snip) { snip == target_snip }
50
+ end
51
+
52
+ let(:snip) { double(CodeSnippet::Snip) }
53
+ let(:target_snip) { double(CodeSnippet::Snip) }
54
+
55
+ let(:snips) do
56
+ [
57
+ snip,
58
+ snip,
59
+ target_snip
60
+ ]
61
+ end
62
+
63
+ before do
64
+ subject.instance_variable_set(:@snippets, snips)
65
+ @result = subject.filter(query)
66
+ end
67
+
68
+ it 'finds target snip in set' do
69
+ expect(@result.count).to be 1
70
+ expect(@result.first).to eq target_snip
71
+ end
72
+
73
+ context 'when no query is defined' do
74
+ before do
75
+ @results = subject.filter
76
+ end
77
+
78
+ it 'returns everything' do
79
+ expect(@results.count).to eq 3
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#find' do
85
+ let(:search_term) { 'foo' }
86
+ let(:lang) { '.rb' }
87
+
88
+ let(:snip) do
89
+ CodeSnippet::Snip.new('path/to/snip.rb', 'snip.rb', '.rb')
90
+ end
91
+
92
+ let(:target_snip) do
93
+ CodeSnippet::Snip.new('path/to/snip.rb', 'foo.rb', '.rb')
94
+ end
95
+
96
+ let(:almost_target_snip) do
97
+ CodeSnippet::Snip.new('path/to/snip.rb', 'foo.go', '.go')
98
+ end
99
+
100
+ let(:snips) do
101
+ [
102
+ snip,
103
+ snip,
104
+ target_snip,
105
+ almost_target_snip
106
+ ]
107
+ end
108
+
109
+ before do
110
+ subject.instance_variable_set(:@snippets, snips)
111
+ @result = subject.find(search_term, lang)
112
+ end
113
+
114
+ it 'returns expected snip' do
115
+ expect(@result.count).to be 1
116
+ expect(@result.first).to eq target_snip
117
+ end
118
+
119
+ context 'when language filter is not provided' do
120
+ let(:lang) { nil }
121
+
122
+ it 'returns expected snips' do
123
+ expect(@result.count).to be 2
124
+ expect(@result[0]).to be target_snip
125
+ expect(@result[1]).to be almost_target_snip
126
+ end
127
+ end
128
+ end
129
+
130
+ describe '#filter_by_extension' do
131
+ let(:search_term) { 'foo' }
132
+ let(:lang) { '.rb' }
133
+
134
+ let(:snip) do
135
+ CodeSnippet::Snip.new('path/to/snip.rb', 'snip.go', '.go')
136
+ end
137
+
138
+ let(:target_snip) do
139
+ CodeSnippet::Snip.new('path/to/snip.rb', 'foo.rb', '.rb')
140
+ end
141
+
142
+ let(:another_target_snip) do
143
+ CodeSnippet::Snip.new('path/to/snip.rb', 'bar.rb', '.rb')
144
+ end
145
+
146
+ let(:snips) do
147
+ [
148
+ snip,
149
+ snip,
150
+ target_snip,
151
+ another_target_snip
152
+ ]
153
+ end
154
+
155
+ before do
156
+ subject.instance_variable_set(:@snippets, snips)
157
+ @result = subject.filter_by_extension(lang)
158
+ end
159
+
160
+ it 'returns files matching extension' do
161
+ expect(@result.count).to be 2
162
+ expect(@result[0]).to be target_snip
163
+ expect(@result[1]).to be another_target_snip
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CodeSnippet::Snip do
4
+ let(:file_name) { 'snippet-file' }
5
+ let(:file_ext) { '.ext' }
6
+ let(:snip_dir) { 'path/to/snippets' }
7
+ let(:path) { "#{snip_dir}/path/to/#{file_name}#{file_ext}" }
8
+
9
+ describe '.new_from_file' do
10
+ before do
11
+ allow(CodeSnippet::CLI).to receive(:snip_dir)
12
+ .and_return(snip_dir)
13
+
14
+ @snip = described_class.new_from_file(path)
15
+ end
16
+
17
+ it 'creates snip from path' do
18
+ expect(@snip.path).to eq path
19
+ expect(@snip.name).to eq file_name
20
+ expect(@snip.ext).to eq file_ext
21
+ end
22
+ end
23
+
24
+ subject { described_class.new_from_file(path) }
25
+
26
+ describe '#name' do
27
+ it 'returns file name without snip_dir or file extension' do
28
+ expect(subject.name).to eq file_name
29
+ end
30
+ end
31
+
32
+ describe '#exist?' do
33
+ let(:exists) { true }
34
+ before do
35
+ allow(File).to receive(:exist?)
36
+ .with(path)
37
+ .and_return(exists)
38
+ end
39
+
40
+ it 'returns true when file exists' do
41
+ expect(subject.exist?).to be true
42
+ end
43
+
44
+ context 'when file does not exist' do
45
+ let(:exists) { false }
46
+
47
+ it 'returns false' do
48
+ expect(subject.exist?).to be false
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '#content' do
54
+ let(:exist) { true }
55
+ let(:content) { 'some-snippet' }
56
+
57
+ before do
58
+ allow(subject).to receive(:exist?).and_return(exist)
59
+ allow(File).to receive(:read)
60
+ .with(path)
61
+ .and_return(content)
62
+ end
63
+
64
+ it 'returns snippet file content' do
65
+ expect(subject.content).to eq content
66
+ end
67
+
68
+ context 'when file does not exist' do
69
+ let(:exist) { false }
70
+
71
+ it 'raises error' do
72
+ expect do
73
+ subject.content
74
+ end.to raise_error('cannot read snippet code')
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'simplecov'
4
+ require 'pry'
5
+ require 'bundler/setup'
6
+
7
+ SimpleCov.start
8
+
9
+ SimpleCov.configure do
10
+ add_filter 'config'
11
+ add_filter 'spec'
12
+ add_filter 'vendor'
13
+ coverage_dir 'target/reports/coverage'
14
+ minimum_coverage 10
15
+ end
16
+
17
+ require 'code_snippet'
18
+ require 'code_snippet/cli'
19
+
20
+ RSpec.configure do |config|
21
+ config.example_status_persistence_file_path = '.rspec_status'
22
+ config.disable_monkey_patching!
23
+
24
+ config.expect_with :rspec do |c|
25
+ c.syntax = :expect
26
+ end
27
+ end