bunchcli 1.1.12 → 1.1.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1eae5ef196f082cf2f09b09b42f91f5bfa2c87f023337483064549907aeb4c47
4
- data.tar.gz: 76a96e232d5d94b12af6073a6c71d5dcd6f652cdeda6a5dad309b903f687deb1
3
+ metadata.gz: 3e569677c996de604f94399e8006421686b41ccbbaac882a1546d4d14a9d071c
4
+ data.tar.gz: eabee141d2bbc1dafc8014d6b6e30ef1bada5fc9ec15846509e2cca4c4a788e5
5
5
  SHA512:
6
- metadata.gz: dce1d77092144c64ff53ad79dbfbf41ce4f669428b9ab9fbf4f9f751e9eb3e538037bf66ad97e50cce88ff95125d5768707f3617a28c4acdb577f9c9987063b4
7
- data.tar.gz: 6a0f47f065fc2d3f1bdf8a562957e9c1bc31b035698abc263892a02877974b773e16694dfb2320a815170fa6aed267bfde581ed64da84653a2d78ff2d8da7dac
6
+ metadata.gz: 5404b8ca066959471a44057905bd1c3d379a6c23b464745b2a061ec93587a8ea6e92a006a7ce8d10bf6704b52bf8e8c9a2f970c8f0b6a9eb6fcc1c7ee22abbea
7
+ data.tar.gz: 89995c81e7256502f74aef2652076ffb6a29266353bdb89788ff568cd27b72410e2aff485a4bb174b8b4d57a2687d3627caa884ebe83fb26ca7677afd9cf725d
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ Brett Terpstra <me@brettterpstra.com>
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ### 1.1.13
2
+
3
+ - Sort Bunches when listing and searching
4
+ - Execute shortest match rather than first match
5
+
1
6
  ### 1.1.11pre
2
7
 
3
8
  - Fix for Terminal when checking for bundle id
data/bin/bunch CHANGED
@@ -1,11 +1,19 @@
1
- #!/usr/bin/env ruby
1
+ #!/usr/bin/env ruby -W1
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'optparse'
4
5
  require 'bunch'
5
6
 
7
+ def app_running?(app)
8
+ !`ps ax|grep -i "#{app}.app"|grep -v grep`.empty?
9
+ end
10
+
11
+ TARGET_APP = app_running?('Bunch Beta') ? 'Bunch Beta' : 'Bunch'
12
+ TARGET_URL = TARGET_APP == 'Bunch Beta' ? 'x-bunch-beta' : 'x-bunch'
13
+
6
14
  def help
7
15
  puts "\nUsage: #{File.basename(__FILE__)} [options] BUNCH_NAME|PATH_TO_FILE"
8
- puts "\nBunch names are case insensitive and will execute first match"
16
+ puts "\nBunch names are case insensitive and will execute shortest match"
9
17
  puts "Use 'bunch -h' to display options"
10
18
  end
11
19
 
@@ -52,20 +60,20 @@ optparse = OptionParser.new do |opts|
52
60
  bunch.variables = opt
53
61
  end
54
62
 
55
- opts.on('--pref', 'Set a preference. Run without argument to list available preferences.') do |opt|
63
+ opts.on('--pref', 'Set a preference. Run without argument to list available preferences.') do
56
64
  bunch.url_method = 'setPref'
57
65
  end
58
66
 
59
- opts.on('-u', '--url', 'Output URL instead of opening') do |_opt|
67
+ opts.on('-u', '--url', 'Output URL instead of opening') do
60
68
  bunch.show_url = true
61
69
  end
62
70
 
63
- opts.on('-i','--interactive', 'Interactively generate a Bunch url') do |opt|
71
+ opts.on('-i', '--interactive', 'Interactively generate a Bunch url') do
64
72
  BunchURLGenerator.new.generate
65
73
  Process.exit 0
66
74
  end
67
75
 
68
- opts.on('--show-config', 'Display all configuration values') do |opt|
76
+ opts.on('--show-config', 'Display all configuration values') do
69
77
  bunch.show_config
70
78
  Process.exit 0
71
79
  end
@@ -75,8 +83,10 @@ optparse = OptionParser.new do |opts|
75
83
  Process.exit 0
76
84
  end
77
85
 
78
- opts.on('-f', '--force-refresh', 'Force refresh cached preferences') do |opt|
86
+ opts.on('-f', '--force-refresh', 'Force refresh cached preferences') do
79
87
  bunch.update_cache
88
+ warn 'Cache refreshed'
89
+ Process.exit 0
80
90
  end
81
91
 
82
92
  opts.on('-h', '--help', 'Display this screen') do |_opt|
@@ -93,14 +103,14 @@ end
93
103
 
94
104
  optparse.parse!
95
105
 
96
- unless ARGV.length > 0
97
- if STDIN.stat.size > 0
106
+ if ARGV.empty?
107
+ if $stdin.stat.size.positive?
98
108
  bunch.url_method = 'raw'
99
- bunch.open(CGI.escape(STDIN.read))
109
+ bunch.open(CGI.escape($stdin.read))
100
110
  elsif bunch.url_method == 'setPref'
101
111
  bunch.list_preferences
102
112
  else
103
- puts "CLI for Bunches.app"
113
+ puts 'CLI for Bunches.app'
104
114
  help
105
115
  end
106
116
  else
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Main Bunch CLI Class
1
4
  class Bunch
2
5
  include Util
3
6
  attr_writer :url_method, :fragment, :variables, :show_url
@@ -15,10 +18,10 @@ class Bunch
15
18
 
16
19
  def launch_if_needed
17
20
  pid = `ps ax | grep 'MacOS/Bunch'|grep -v grep`.strip
18
- if pid == ""
19
- `open -a Bunch`
20
- sleep 2
21
- end
21
+ return unless pid == ''
22
+
23
+ `open -a Bunch`
24
+ sleep 2
22
25
  end
23
26
 
24
27
  def update_cache
@@ -32,20 +35,19 @@ class Bunch
32
35
  'bunches' => bunches,
33
36
  'updated' => Time.now.strftime('%s').to_i
34
37
  }
35
- File.open(target,'w') do |f|
38
+ File.open(target, 'w') do |f|
36
39
  f.puts YAML.dump(settings)
37
40
  end
38
- return settings
41
+
42
+ settings
39
43
  end
40
44
 
41
45
  def get_cache
42
46
  target = File.expand_path(CACHE_FILE)
43
- if File.exists?(target)
47
+ if File.exist?(target)
44
48
  settings = YAML.load(IO.read(target))
45
49
  now = Time.now.strftime('%s').to_i
46
- if now - settings['updated'].to_i > CACHE_TIME
47
- settings = update_cache
48
- end
50
+ settings = update_cache if now - settings['updated'].to_i > CACHE_TIME
49
51
  else
50
52
  settings = update_cache
51
53
  end
@@ -55,14 +57,14 @@ class Bunch
55
57
  end
56
58
 
57
59
  def variable_query
58
- vars = @variables.split(/,/).map { |v| v.strip }
60
+ vars = @variables.split(/,/).map(&:strip)
59
61
  query = []
60
- vars.each { |v|
61
- parts = v.split(/=/).map { |v| v.strip }
62
+ vars.each do |v|
63
+ parts = v.split(/=/).map(&:strip)
62
64
  k = parts[0]
63
65
  v = parts[1]
64
66
  query << "#{k}=#{CGI.escape(v)}"
65
- }
67
+ end
66
68
  query
67
69
  end
68
70
 
@@ -72,11 +74,11 @@ class Bunch
72
74
  `osascript -e 'tell app "#{TARGET_APP}" to list bunches'`.strip.split(/,/).each do |b|
73
75
  b.strip!
74
76
  items.push(
75
- path: File.join(bunch_dir, b + '.bunch'),
77
+ path: File.join(bunch_dir, "#{b}.bunch"),
76
78
  title: b
77
79
  )
78
80
  end
79
- items
81
+ items.sort_by { |b| b[:title].downcase }
80
82
  end
81
83
 
82
84
  def bunch_dir
@@ -97,14 +99,16 @@ class Bunch
97
99
  end
98
100
 
99
101
  def url(bunch)
102
+ bunch = CGI.escape(bunch).gsub(/\+/, '%20')
100
103
  params = "&x-success=#{@success}" if @success
101
- if url_method == 'file'
104
+ case url_method
105
+ when /file/
102
106
  %(#{TARGET_URL}://raw?file=#{bunch}#{params})
103
- elsif url_method == 'raw'
107
+ when /raw/
104
108
  %(#{TARGET_URL}://raw?txt=#{bunch}#{params})
105
- elsif url_method == 'snippet'
109
+ when /snippet/
106
110
  %(#{TARGET_URL}://snippet?file=#{bunch}#{params})
107
- elsif url_method == 'setPref'
111
+ when /setPref/
108
112
  %(#{TARGET_URL}://setPref?#{bunch})
109
113
  else
110
114
  %(#{TARGET_URL}://#{url_method}?bunch=#{bunch}#{params})
@@ -122,30 +126,24 @@ class Bunch
122
126
  end
123
127
 
124
128
  def find_bunch(str)
125
- found_bunch = false
129
+ matches = []
126
130
 
127
- bunches.each do |bunch|
128
- if bunch[:title].downcase =~ /.*?#{str}.*?/i
129
- found_bunch = bunch
130
- break
131
- end
132
- end
133
- found_bunch
131
+ bunches.each { |bunch| matches.push(bunch) if bunch[:title].downcase =~ /.*?#{str}.*?/i }
132
+ matches.min_by(&:length)
134
133
  end
135
134
 
136
135
  def human_action
137
- (url_method.gsub(/e$/, '') + 'ing').capitalize
136
+ "#{url_method.gsub(/e$/, '')}ing".capitalize
138
137
  end
139
138
 
140
139
  def list_preferences
141
- prefs =<<EOF
142
- toggleBunches=[0,1] Allow Bunches to be both opened and closed
143
- configDir=[path] Absolute path to Bunches folder
144
- singleBunchMode=[0,1] Close open Bunch when opening new one
145
- preserveOpenBunches=[0,1] Restore Open Bunches on Launch
146
- debugLevel=[0-4] Set the logging level for the Bunch Log
147
- EOF
148
- puts prefs
140
+ puts <<~EOHELP
141
+ toggleBunches=[0,1] Allow Bunches to be both opened and closed
142
+ configDir=[path] Absolute path to Bunches folder
143
+ singleBunchMode=[0,1] Close open Bunch when opening new one
144
+ preserveOpenBunches=[0,1] Restore Open Bunches on Launch
145
+ debugLevel=[0-4] Set the logging level for the Bunch Log
146
+ EOHELP
149
147
  end
150
148
 
151
149
 
@@ -153,39 +151,40 @@ EOF
153
151
  launch_if_needed
154
152
  # get front app
155
153
  front_app = %x{osascript -e 'tell application "System Events" to return name of first application process whose frontmost is true'}.strip
156
- bid = bundle_id(front_app) rescue false
157
- @success = bid if (bid)
154
+ bid = bundle_id(front_app) || false
155
+ @success = bid if bid
158
156
 
159
- if @url_method == 'raw'
157
+ case @url_method
158
+ when /raw/
160
159
  warn 'Running raw string'
161
160
  if @show_url
162
161
  $stdout.puts url(str)
163
162
  else
164
163
  `open '#{url(str)}'`
165
164
  end
166
- elsif @url_method == 'snippet'
167
- _url = url(str)
165
+ when /snippet/
166
+ this_url = url(str)
168
167
  params = []
169
168
  params << "fragment=#{CGI.escape(@fragment)}" if @fragment
170
169
  params.concat(variable_query) if @variables
171
- _url += '&' + params.join('&')
170
+ this_url += "&#{params.join('&')}" if params.length.positive?
172
171
  if @show_url
173
- $stdout.puts _url
172
+ $stdout.puts this_url
174
173
  else
175
- warn "Opening snippet"
176
- `open '#{_url}'`
174
+ warn 'Opening snippet'
175
+ `open '#{this_url}'`
177
176
  end
178
- elsif @url_method == 'setPref'
177
+ when /setPref/
179
178
  if str =~ /^(\w+)=([^= ]+)$/
180
- _url = url(str)
179
+ this_url = url(str)
181
180
  if @show_url
182
- $stdout.puts _url
181
+ $stdout.puts this_url
183
182
  else
184
183
  warn "Setting preference #{str}"
185
- `open '#{_url}'`
184
+ `open '#{this_url}'`
186
185
  end
187
186
  else
188
- warn "Invalid key=value pair"
187
+ warn 'Invalid key=value pair'
189
188
  Process.exit 1
190
189
  end
191
190
  else
@@ -193,30 +192,28 @@ EOF
193
192
  params = []
194
193
  params << "fragment=#{CGI.escape(@fragment)}" if @fragment
195
194
  params.concat(variable_query) if @variables
196
- unless bunch
197
- if File.exists?(str)
198
- @url_method = 'file'
199
- _url = url(str)
200
- _url += '&' + params.join('&') if params.length
201
- if @show_url
202
- $stdout.puts _url
203
- else
204
- warn "Opening file"
205
- `open '#{_url}'`
206
- end
195
+ if bunch
196
+ this_url = url(bunch[:title])
197
+ this_url += "&#{params.join('&')}" if params.length
198
+ if @show_url
199
+ $stdout.puts this_url
207
200
  else
208
- warn 'No matching Bunch found'
209
- Process.exit 1
201
+ warn "#{human_action} #{bunch[:title]}"
202
+ `open '#{this_url}'`
210
203
  end
211
- else
212
- _url = url(bunch[:title])
213
- _url += '&' + params.join('&') if params.length
204
+ elsif File.exist?(str)
205
+ @url_method = 'file'
206
+ this_url = url(str)
207
+ this_url += "&#{params.join('&')}" if params.length
214
208
  if @show_url
215
- $stdout.puts _url
209
+ $stdout.puts this_url
216
210
  else
217
- warn "#{human_action} #{bunch[:title]}"
218
- `open '#{_url}'`
211
+ warn 'Opening file'
212
+ `open '#{this_url}'`
219
213
  end
214
+ else
215
+ warn 'No matching Bunch found'
216
+ Process.exit 1
220
217
  end
221
218
  end
222
219
  # attempt to restore front app
@@ -229,7 +226,7 @@ EOF
229
226
  puts output
230
227
  end
231
228
 
232
- def show_config(key=nil)
229
+ def show_config(key = nil)
233
230
  case key
234
231
  when /(folder|dir)/
235
232
  puts bunch_dir
@@ -240,10 +237,8 @@ EOF
240
237
  else
241
238
  puts "Bunches Folder: #{bunch_dir}"
242
239
  puts "Default URL Method: #{url_method}"
243
- puts "Cached Bunches"
244
- bunches.each {|b|
245
- puts " - #{b[:title]}"
246
- }
240
+ puts 'Cached Bunches'
241
+ bunches.each { |b| puts " - #{b[:title]}" }
247
242
  end
248
243
  end
249
244
  end
@@ -15,25 +15,31 @@ end
15
15
  module Util
16
16
  def bundle_id(app)
17
17
  shortname = app.sub(/\.app$/, '')
18
- apps = `mdfind -onlyin /Applications -onlyin /Applications/Setapp -onlyin /Applications/Utilities -onlyin ~/Applications -onlyin /Developer/Applications -onlyin /System/Applications 'kMDItemKind==Application'`
18
+ app_dirs = [
19
+ '/Applications',
20
+ '/Applications/Setapp',
21
+ '/Applications/Utilities',
22
+ '~/Applications',
23
+ '/Developer/Applications',
24
+ '/System/Applications'
25
+ ]
26
+ only_in = app_dirs.map { |dir| "-onlyin #{dir}" }.join(' ')
27
+ apps = `mdfind #{only_in} 'kMDItemKind==Application'`
19
28
 
20
- return false if !apps || apps.strip.length == 0
29
+ return false if !apps || apps.strip.empty?
21
30
 
22
31
  foundapps = apps.split(/\n/).select! { |line| line.chomp =~ /#{shortname}\.app$/i }
23
32
 
24
- if foundapps.length > 0
25
- foundapp = foundapps[0]
26
- else
27
- return false
28
- end
33
+ return false if foundapps.empty?
34
+
35
+ foundapp = foundapps[0]
29
36
 
30
37
  if foundapp
31
- bid = `mdls -name kMDItemCFBundleIdentifier -r "#{foundapp}"`.chomp
38
+ `mdls -name kMDItemCFBundleIdentifier -r "#{foundapp}"`.chomp
32
39
  else
33
40
  # warn "Could not locate bundle id for #{shortname}, using provided app name"
34
- bid = app
41
+ app
35
42
  end
36
- bid
37
43
  end
38
44
  end
39
45
 
@@ -80,10 +86,43 @@ module Prompt
80
86
  lines.join("\n").chomp
81
87
  end
82
88
 
89
+ def yn(question, default_response: false)
90
+ default = default_response || 'n'
91
+
92
+ # if this isn't an interactive shell, answer default
93
+ return default.downcase == 'y' unless $stdout.isatty
94
+
95
+ # clear the buffer
96
+ if ARGV&.length
97
+ ARGV.length.times do
98
+ ARGV.shift
99
+ end
100
+ end
101
+ system 'stty cbreak'
102
+
103
+ options = if default
104
+ default =~ /y/i ? '[Y/n]' : '[y/N]'
105
+ else
106
+ '[y/n]'
107
+ end
108
+
109
+ $stdout.syswrite "#{question.sub(/\?$/, '')} #{options}? "
110
+ res = $stdin.sysread 1
111
+ puts
112
+ system 'stty cooked'
113
+
114
+ res.chomp!
115
+ res.downcase!
116
+
117
+ res = default.downcase if res == ''
118
+
119
+ res =~ /y/i
120
+ end
121
+
83
122
  def url_encode_text
84
123
  text = get_text
85
124
  puts
86
- CGI.escape(text)
125
+ CGI.escape(text).gsub(/\+/, '%20')
87
126
  end
88
127
  end
89
128
 
@@ -109,17 +148,17 @@ class Menu
109
148
 
110
149
  def choose(query = 'Select an item')
111
150
  throw 'No items initialized' if @items.nil?
112
- STDERR.puts
113
- STDERR.puts "┌#{("" * 74)}┐"
114
- intpad = Math::log10(@items.length).to_i + 1
151
+ $stderr.puts
152
+ warn "┌#{'' * 74}┐"
153
+ intpad = Math.log10(@items.length).to_i + 1
115
154
  @items.each_with_index do |item, idx|
116
- idxstr = "%#{intpad}d" % (idx + 1)
155
+ idxstr = format("%#{intpad}d", idx + 1)
117
156
  line = "#{idxstr}: #{item.title}"
118
157
  pad = 74 - line.length
119
- STDERR.puts "│#{line}#{" " * pad}│"
158
+ warn "│#{line}#{' ' * pad}│"
120
159
  end
121
- STDERR.puts "└┤ #{query} ├#{"" * (70 - query.length)}┘"
122
- sel = choose_number("> ", @items.length)
160
+ warn "└┤ #{query} ├#{'' * (70 - query.length)}┘"
161
+ sel = choose_number('> ', @items.length)
123
162
  sel ? @items[sel.to_i - 1] : nil
124
163
  end
125
164
  end
@@ -130,13 +169,13 @@ class Snippet
130
169
  def initialize(file)
131
170
  if File.exist?(File.expand_path(file))
132
171
  @contents = IO.read(File.expand_path(file))
133
- @fragments = fragments
172
+ @fragments = find_fragments
134
173
  else
135
- throw ('Tried to initialize snippet with invalid file')
174
+ throw 'Tried to initialize snippet with invalid file'
136
175
  end
137
176
  end
138
177
 
139
- def fragments
178
+ def find_fragments
140
179
  rx = /(?i-m)(?:[-#]+)\[([\s\S]*?)\][-# ]*\n([\s\S]*?)(?=\n(?:-+\[|#+\[|$))/
141
180
  matches = @contents.scan(rx)
142
181
  fragments = {}
@@ -167,6 +206,7 @@ class BunchFinder
167
206
 
168
207
  def initialize
169
208
  config_dir = `osascript -e 'tell app "#{TARGET_APP}" to get preference "Folder"'`.strip
209
+ config_dir.sub!(%r{^file://}, '')
170
210
  config_dir = File.expand_path(config_dir)
171
211
  if File.directory?(config_dir)
172
212
  @config_dir = config_dir
@@ -177,10 +217,8 @@ class BunchFinder
177
217
 
178
218
  def bunches_to_items
179
219
  items = []
180
- `osascript -e 'tell app "#{TARGET_APP}" to list bunches'`.strip.split(/,/).each do |b|
181
- filename = b.strip
182
- items << MenuItem.new(filename, filename, filename)
183
- end
220
+ bunches = `osascript -e 'tell app "#{TARGET_APP}" to list bunches'`.strip.split(/,/).map(&:strip)
221
+ bunches.sort_by(&:downcase).each { |b| items << MenuItem.new(b, b, b) }
184
222
  items
185
223
  end
186
224
 
@@ -307,8 +345,15 @@ class BunchURLGenerator
307
345
  parameters << ['x-delay', delay.to_s] if delay =~ /^\d+$/
308
346
  end
309
347
 
310
- query_string = parameters.map { |param| "#{param[0]}=#{param[1]}" }.join('&')
348
+ query_string = parameters.map { |param| "#{param[0]}=#{param[1].gsub(/\+/, '%20')}" }.join('&')
349
+ full_url = "#{url}?#{query_string}".strip
311
350
 
312
- puts url + '?' + query_string
351
+ res = yn('Copy URL to clipboard')
352
+ if res
353
+ `echo '#{full_url}'|tr -d '\n'|pbcopy`
354
+ warn 'Copied to clipboard'
355
+ else
356
+ puts full_url
357
+ end
313
358
  end
314
359
  end
data/lib/bunch/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module BunchCLI
2
- VERSION = "1.1.12"
4
+ VERSION = '1.1.14'
3
5
  end
data/lib/bunch.rb CHANGED
@@ -1,10 +1,7 @@
1
1
  CACHE_TIME = 86400 #seconds, 1 day = 86400
2
2
  CACHE_FILE = "~/.bunch_cli_cache"
3
- TARGET_APP = "Bunch"
4
3
 
5
- TARGET_URL = TARGET_APP == 'Bunch Beta' ? 'x-bunch-beta' : 'x-bunch'
6
-
7
- require "bunch/version"
4
+ require 'bunch/version'
8
5
  require 'yaml'
9
6
  require 'cgi'
10
7
  require 'bunch/url_generator'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bunchcli
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.12
4
+ version: 1.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-22 00:00:00.000000000 Z
11
+ date: 2022-07-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -19,6 +19,7 @@ extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
21
  - ".gitignore"
22
+ - AUTHORS
22
23
  - CHANGELOG.md
23
24
  - Gemfile
24
25
  - LICENSE.txt