marked-conductor 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 +7 -0
- data/.irbrc +3 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +11 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +58 -0
- data/LICENSE.txt +21 -0
- data/README.md +145 -0
- data/Rakefile +57 -0
- data/bin/conductor +54 -0
- data/images/preferences.jpg +0 -0
- data/lib/conductor/array.rb +12 -0
- data/lib/conductor/command.rb +53 -0
- data/lib/conductor/condition.rb +248 -0
- data/lib/conductor/config.rb +47 -0
- data/lib/conductor/env.rb +54 -0
- data/lib/conductor/hash.rb +11 -0
- data/lib/conductor/script.rb +59 -0
- data/lib/conductor/string.rb +62 -0
- data/lib/conductor/version.rb +5 -0
- data/lib/conductor.rb +25 -0
- data/marked-conductor.gemspec +40 -0
- data/src/_README.md +147 -0
- metadata +127 -0
@@ -0,0 +1,248 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Conductor
|
4
|
+
class Condition
|
5
|
+
def initialize(condition)
|
6
|
+
@condition = condition
|
7
|
+
@env = Conductor::Env.env
|
8
|
+
end
|
9
|
+
|
10
|
+
def true?
|
11
|
+
parse_condition
|
12
|
+
end
|
13
|
+
|
14
|
+
def split_booleans(condition)
|
15
|
+
split = condition.split(/ ((?:AND )?NOT|AND|OR) /)
|
16
|
+
|
17
|
+
if split.count == 1
|
18
|
+
test_condition(split[0])
|
19
|
+
else
|
20
|
+
res = nil
|
21
|
+
bool = nil
|
22
|
+
prev = false
|
23
|
+
split.each do |cond|
|
24
|
+
if cond =~ /((?:AND )?NOT|AND|OR)/
|
25
|
+
bool = cond.bool_to_symbol
|
26
|
+
next
|
27
|
+
end
|
28
|
+
|
29
|
+
r = split_booleans(cond)
|
30
|
+
|
31
|
+
if bool == :and && (!r || !prev)
|
32
|
+
res = false
|
33
|
+
elsif bool == :or && (r || prev)
|
34
|
+
return true
|
35
|
+
elsif bool == :not && (r || prev)
|
36
|
+
res = false
|
37
|
+
else
|
38
|
+
res = r
|
39
|
+
end
|
40
|
+
|
41
|
+
prev = res
|
42
|
+
end
|
43
|
+
res
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_operator(value1, value2, operator)
|
48
|
+
case operator
|
49
|
+
when :gt
|
50
|
+
value1.to_f > value2.to_f
|
51
|
+
when :lt
|
52
|
+
value1.to_f < value2.to_f
|
53
|
+
when :contains
|
54
|
+
value1.to_s =~ /#{value2}/i
|
55
|
+
when :starts_with
|
56
|
+
value1.to_s =~ /^#{value2}/i
|
57
|
+
when :ends_with
|
58
|
+
value1.to_s =~ /#{value2}$/i
|
59
|
+
when :not_equal
|
60
|
+
value1 != value2
|
61
|
+
when :equal
|
62
|
+
value1 == value2
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def split_condition(condition)
|
67
|
+
res = condition.match(/^(?<val1>.*?)(?:(?: +(?<op>(?:is|does)(?: not)?(?: an?|type(?: of)?|equals?(?: to))?|!?==?|[gl]t|(?:greater|less)(?: than)?|<|>|(?:starts|ends) with|(?:ha(?:s|ve) )?(?:prefix|suffix)|has|contains?|includes?) +)(?<val2>.*?))?$/i)
|
68
|
+
[res['val1'], res['val2'], operator_to_symbol(res['op'])]
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_type(val1, val2, operator)
|
72
|
+
res = case val2
|
73
|
+
when /array/i
|
74
|
+
val1.is_a?(Array)
|
75
|
+
when /(string|text)/i
|
76
|
+
val1.is_a?(String)
|
77
|
+
when /date/i
|
78
|
+
val1.date?
|
79
|
+
end
|
80
|
+
operator == :type_of ? res : !res
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_string(val1, val2, operator)
|
84
|
+
return operator == :not_equal ? val1.nil? : !val1.nil? if val2.nil?
|
85
|
+
|
86
|
+
return operator == :not_equal if val1.nil?
|
87
|
+
|
88
|
+
if val1.date?
|
89
|
+
if val2.time?
|
90
|
+
date1 = val1.to_date
|
91
|
+
date2 = val2.to_date
|
92
|
+
else
|
93
|
+
date1 = operator == :gt ? val1.to_day(:end) : val1.to_day
|
94
|
+
date2 = operator == :gt ? val2.to_day(:end) : val1.to_day
|
95
|
+
end
|
96
|
+
|
97
|
+
res = case operator
|
98
|
+
when :gt
|
99
|
+
date1 > date2
|
100
|
+
when :lt
|
101
|
+
date1 < date2
|
102
|
+
when :equal
|
103
|
+
date1 == date2
|
104
|
+
when :not_equal
|
105
|
+
date1 != date2
|
106
|
+
end
|
107
|
+
return res unless res.nil?
|
108
|
+
end
|
109
|
+
|
110
|
+
val2 = if val2.strip =~ %r{^/.*?/$}
|
111
|
+
val2.gsub(%r{(^/|/$)}, '')
|
112
|
+
else
|
113
|
+
Regexp.escape(val2)
|
114
|
+
end
|
115
|
+
|
116
|
+
case operator
|
117
|
+
when :contains
|
118
|
+
val1.to_s =~ /#{val2}/i
|
119
|
+
when :not_starts_with
|
120
|
+
val1.to_s !~ /^#{val2}/i
|
121
|
+
when :not_ends_with
|
122
|
+
val1.to_s !~ /#{val2}$/i
|
123
|
+
when :starts_with
|
124
|
+
val1.to_s =~ /^#{val2}/i
|
125
|
+
when :ends_with
|
126
|
+
val1.to_s =~ /#{val2}$/i
|
127
|
+
when :equal
|
128
|
+
val1.to_s =~ /^#{val2}$/i
|
129
|
+
when :not_equal
|
130
|
+
val1.to_s !~ /^#{val2}$/i
|
131
|
+
else
|
132
|
+
false
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_tree(origin, value, operator)
|
137
|
+
return true if File.exist?(File.join(origin, value))
|
138
|
+
|
139
|
+
dir = File.dirname(origin)
|
140
|
+
|
141
|
+
if Dir.exist?(File.join(dir, value))
|
142
|
+
true
|
143
|
+
elsif [Dir.home, '/'].include?(dir)
|
144
|
+
false
|
145
|
+
else
|
146
|
+
test_tree(dir, value, operator)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_truthy(value1, value2, operator)
|
151
|
+
return false unless value2.bool?
|
152
|
+
|
153
|
+
value2.to_bool!
|
154
|
+
|
155
|
+
res = value1 == value2
|
156
|
+
|
157
|
+
operator == :not_equal ? !res : res
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_condition(condition)
|
161
|
+
type, value, operator = split_condition(condition)
|
162
|
+
|
163
|
+
if operator.nil?
|
164
|
+
return case type
|
165
|
+
when /^(true|any|all|else|\*+|catch(all)?)$/i
|
166
|
+
true
|
167
|
+
else
|
168
|
+
false
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
case type
|
173
|
+
when /^ext/i
|
174
|
+
test_string(@env[:ext], value, operator) ? true : false
|
175
|
+
when /^tree/i
|
176
|
+
test_tree(@env[:origin], value, operator)
|
177
|
+
when /^(path|dir)/i
|
178
|
+
test_string(@env[:origin], value, operator) ? true : false
|
179
|
+
when /^phase/i
|
180
|
+
test_string(@env[:phase], value, :starts_with) ? true : false
|
181
|
+
when /^text/i
|
182
|
+
test_string(IO.read(@env[:filepath]), value, operator) ? true : false
|
183
|
+
when /^(yaml|headers|frontmatter)(?::(.*?))?$/i
|
184
|
+
m = Regexp.last_match
|
185
|
+
content = IO.read(@env[:filepath])
|
186
|
+
return false unless content =~ /^---/
|
187
|
+
|
188
|
+
yaml = YAML.safe_load(content.split(/(---|\.\.\.)/)[1])
|
189
|
+
if m[2]
|
190
|
+
value1 = yaml[m[2]]
|
191
|
+
value1 = value1.join(',') if value1.is_a?(Array)
|
192
|
+
if %i[type_of not_type_of].include?(operator)
|
193
|
+
test_type(value1, value, operator)
|
194
|
+
elsif value1.is_a?(Boolean)
|
195
|
+
test_truthy(value1, value, operator)
|
196
|
+
elsif value1.number? && value2.number? && %i[gt lt equal not_equal].include?(operator)
|
197
|
+
test_operator(value1, value, operator)
|
198
|
+
else
|
199
|
+
test_string(value1, value, operator)
|
200
|
+
end
|
201
|
+
else
|
202
|
+
res = value? ? yaml.key?(value) : true
|
203
|
+
operator == :not_equal ? !res : res
|
204
|
+
end
|
205
|
+
else
|
206
|
+
false
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def operator_to_symbol(operator)
|
211
|
+
return operator if operator.nil?
|
212
|
+
|
213
|
+
case operator
|
214
|
+
when /(gt|greater( than)?|>|(?:is )?after)/i
|
215
|
+
:gt
|
216
|
+
when /(lt|less( than)?|<|(?:is )?before)/i
|
217
|
+
:lt
|
218
|
+
when /(ha(?:s|ve)|contains|includes|match(es)?|\*=)/i
|
219
|
+
:contains
|
220
|
+
when /not (suffix|ends? with)/i
|
221
|
+
:not_ends_with
|
222
|
+
when /not (prefix|(starts?|begins?) with)/i
|
223
|
+
:not_starts_with
|
224
|
+
when /(suffix|ends with|\$=)/i
|
225
|
+
:ends_with
|
226
|
+
when /(prefix|(starts?|begins?) with|\^=)/i
|
227
|
+
:starts_with
|
228
|
+
when /is not (an?|type( of)?)/i
|
229
|
+
:not_type_of
|
230
|
+
when /is (an?|type( of)?)/i
|
231
|
+
:type_of
|
232
|
+
when /((?:(?:is|does) )?not(?: equals?)?|!==?)/i
|
233
|
+
:not_equal
|
234
|
+
when /(is|==?|equals?)/i
|
235
|
+
:equal
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def parse_condition
|
240
|
+
cond = @condition.to_s.gsub(/\((.*?)\)/) do
|
241
|
+
condition = Regexp.last_match(1)
|
242
|
+
split_booleans(condition)
|
243
|
+
end
|
244
|
+
|
245
|
+
split_booleans(cond)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Conductor
|
4
|
+
# Configuration methods
|
5
|
+
class Config
|
6
|
+
attr_reader :config, :tracks
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
config_file = File.expand_path('~/.config/conductor/tracks.yaml')
|
10
|
+
|
11
|
+
create_config(config_file) unless File.exist?(config_file)
|
12
|
+
|
13
|
+
@config ||= YAML.safe_load(IO.read(config_file))
|
14
|
+
|
15
|
+
@tracks = @config['tracks'].symbolize_keys
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_config(config_file)
|
20
|
+
config_dir = File.dirname(config_file)
|
21
|
+
scripts_dir = File.dirname(File.join(config_file, 'scripts'))
|
22
|
+
FileUtils.mkdir_p(config_dir) unless File.directory?(config_dir)
|
23
|
+
FileUtils.mkdir_p(scripts_dir) unless File.directory?(scripts_dir)
|
24
|
+
File.open(config_file, 'w') { |f| f.puts sample_config }
|
25
|
+
puts "Sample config created at #{config_file}"
|
26
|
+
|
27
|
+
Process.exit 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def sample_config
|
31
|
+
<<~EOCONFIG
|
32
|
+
tracks:
|
33
|
+
- condition: phase is pre
|
34
|
+
tracks:
|
35
|
+
- condition: tree contains .obsidian
|
36
|
+
tracks:
|
37
|
+
- condition: extension is md
|
38
|
+
script: obsidian-md-filter
|
39
|
+
- condition: extension is md
|
40
|
+
command: rdiscount $file
|
41
|
+
- condition: yaml includes comments
|
42
|
+
script: blog-processor
|
43
|
+
- condition: any
|
44
|
+
command: echo 'NOCUSTOM'
|
45
|
+
EOCONFIG
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Conductor
|
4
|
+
module Env
|
5
|
+
def self.env
|
6
|
+
@env ||= if ENV['CONDUCTOR_TEST'] == 'true'
|
7
|
+
load_test_env
|
8
|
+
else
|
9
|
+
@env ||= {
|
10
|
+
home: ENV['HOME'],
|
11
|
+
css_path: ENV['MARKED_CSS_PATH'],
|
12
|
+
ext: ENV['MARKED_EXT'],
|
13
|
+
includes: ENV['MARKED_INCLUDES'],
|
14
|
+
origin: ENV['MARKED_ORIGIN'],
|
15
|
+
filepath: ENV['MARKED_PATH'],
|
16
|
+
phase: ENV['MARKED_PHASE'],
|
17
|
+
outline: ENV['OUTLINE'],
|
18
|
+
path: ENV['PATH']
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
@env
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.load_test_env
|
26
|
+
@env = {
|
27
|
+
home: '/Users/ttscoff',
|
28
|
+
css_path: '/Applications/Marked 2.app/Contents/Resources/swiss.css',
|
29
|
+
ext: 'md',
|
30
|
+
includes: [],
|
31
|
+
origin: '/Users/ttscoff/Library/Mobile Documents/9CR7T2DMDG~com~ngocluu~onewriter/Documents/nvALT2.2/',
|
32
|
+
filepath: '/Users/ttscoff/Library/Mobile Documents/9CR7T2DMDG~com~ngocluu~onewriter/Documents/nvALT2.2/bt.com App Review- AeroPress timer for iPhone.md',
|
33
|
+
phase: 'PREPROCESS',
|
34
|
+
outline: 'NONE',
|
35
|
+
path: '/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/Users/ttscoff/Library/Mobile Documents/9CR7T2DMDG~com~ngocluu~onewriter/Documents/nvALT2.2'
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.to_s
|
40
|
+
out_h = {
|
41
|
+
'HOME' => @env[:home],
|
42
|
+
'MARKED_CSS_PATH' => @env[:css_path],
|
43
|
+
'MARKED_EXT' => @env[:ext],
|
44
|
+
'MARKED_ORIGIN' => @env[:origin],
|
45
|
+
'MARKED_INCLUDES' => @env[:includes],
|
46
|
+
'MARKED_PATH' => @env[:filepath],
|
47
|
+
'MARKED_PHASE' => @env[:phase],
|
48
|
+
'OUTLINE' => @env[:outline],
|
49
|
+
'PATH'=> @env[:path]
|
50
|
+
}
|
51
|
+
out_h.map { |k, v| %(#{k}="#{v}") }.join(' ')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Conductor
|
4
|
+
# Script runner
|
5
|
+
class Script
|
6
|
+
attr_reader :args, :path
|
7
|
+
|
8
|
+
def initialize(script)
|
9
|
+
parts = Shellwords.split(script)
|
10
|
+
self.path = parts[0]
|
11
|
+
self.args = parts[1..].join(' ')
|
12
|
+
end
|
13
|
+
|
14
|
+
def path=(path)
|
15
|
+
@path = if path =~ %r{^[%/]}
|
16
|
+
File.expand_path(path)
|
17
|
+
else
|
18
|
+
script_dir = File.expand_path('~/.config/conductor/scripts')
|
19
|
+
if File.exist?(File.join(script_dir, path))
|
20
|
+
File.join(script_dir, path)
|
21
|
+
elsif TTY::Which.exist?(path)
|
22
|
+
TTY::Which.which(path)
|
23
|
+
else
|
24
|
+
raise "Path to #{path} not found"
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def args=(array)
|
31
|
+
@args = if array.is_a?(Array)
|
32
|
+
array.join(' ')
|
33
|
+
else
|
34
|
+
array
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def run
|
39
|
+
stdin = Conductor.stdin
|
40
|
+
|
41
|
+
raise 'Script path not defined' unless @path
|
42
|
+
|
43
|
+
use_stdin = true
|
44
|
+
if args =~ /\$\{?file\}?/
|
45
|
+
use_stdin = false
|
46
|
+
args.sub!(/\$\{?file\}?/, Env.env[:filepath])
|
47
|
+
else
|
48
|
+
raise 'No input' unless stdin
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
if use_stdin
|
53
|
+
`echo #{Shellwords.escape(stdin)} | #{Env} #{path} #{args}`
|
54
|
+
else
|
55
|
+
`#{Env} #{path} #{args}`
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# String helpers
|
4
|
+
class ::String
|
5
|
+
def bool_to_symbol
|
6
|
+
case self
|
7
|
+
when /NOT/
|
8
|
+
:not
|
9
|
+
when /AND/
|
10
|
+
:and
|
11
|
+
else
|
12
|
+
:or
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def date?
|
17
|
+
match(/^\d{4}-\d{2}-\d{2}/) ? true : false
|
18
|
+
end
|
19
|
+
|
20
|
+
def time?
|
21
|
+
match(/ \d{1,2}(:\d\d)? *([ap]m)?/i)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_date
|
25
|
+
Chronic.parse(self)
|
26
|
+
end
|
27
|
+
|
28
|
+
def strip_time
|
29
|
+
sub(/ \d{1,2}(:\d\d)? *([ap]m)?/i, '')
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_day(time = :end)
|
33
|
+
t = time == :end ? '23:59' : '00:00'
|
34
|
+
Chronic.parse("#{self.strip_time} #{t}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def number?
|
38
|
+
to_f > 0
|
39
|
+
end
|
40
|
+
|
41
|
+
def bool?
|
42
|
+
match(/^(?:y(?:es)?|no?|t(?:rue)?|f(?:alse)?)$/) ? true : false
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_bool!
|
46
|
+
replace to_bool
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
## Returns a bool representation of the string.
|
51
|
+
##
|
52
|
+
## @return [Boolean] Bool representation of the object.
|
53
|
+
##
|
54
|
+
def to_bool
|
55
|
+
case self
|
56
|
+
when /^[yt]/i
|
57
|
+
true
|
58
|
+
else
|
59
|
+
false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/conductor.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tty-which'
|
4
|
+
require 'yaml'
|
5
|
+
require 'shellwords'
|
6
|
+
require 'fcntl'
|
7
|
+
require 'time'
|
8
|
+
require 'chronic'
|
9
|
+
require 'fileutils'
|
10
|
+
require_relative 'conductor/env'
|
11
|
+
require_relative 'conductor/config'
|
12
|
+
require_relative 'conductor/hash'
|
13
|
+
require_relative 'conductor/array'
|
14
|
+
require_relative 'conductor/string'
|
15
|
+
require_relative 'conductor/script'
|
16
|
+
require_relative 'conductor/command'
|
17
|
+
require_relative 'conductor/condition'
|
18
|
+
|
19
|
+
module Conductor
|
20
|
+
class << self
|
21
|
+
def stdin
|
22
|
+
@stdin ||= $stdin.read.strip if $stdin.stat.size.positive? || $stdin.fcntl(Fcntl::F_GETFL, 0).zero?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/conductor/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "marked-conductor"
|
7
|
+
spec.version = Conductor::VERSION
|
8
|
+
spec.authors = ["Brett Terpstra"]
|
9
|
+
spec.email = ["me@brettterpstra.com"]
|
10
|
+
|
11
|
+
spec.summary = "A custom processor manager for Marked 2 (Mac)"
|
12
|
+
spec.description = "Conductor allows easy configuration of multiple scripts that are run as custom pre/processors for Marked based on conditional statements."
|
13
|
+
spec.homepage = "https://github.com/ttscoff/marked-conductor"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
16
|
+
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/ttscoff/marked-conductor"
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/ttscoff/marked-conductor/CHANGELOG.md"
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
24
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
25
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
spec.bindir = "bin"
|
29
|
+
spec.executables = spec.files.grep(%r{\Abin/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
spec.add_development_dependency "pry", "~> 0.14.2"
|
33
|
+
spec.add_development_dependency "awesome_print", "~> 1.9.2"
|
34
|
+
|
35
|
+
# Uncomment to register a new dependency of your gem
|
36
|
+
spec.add_dependency "tty-which", "~> 0.5.0"
|
37
|
+
spec.add_dependency "chronic", "~> 0.10.2"
|
38
|
+
# For more information and examples about making a new gem, checkout our
|
39
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
40
|
+
end
|