hookapp 0.0.7 → 2.0.3

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +14 -0
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +2 -2
  6. data/LICENSE.md +31 -0
  7. data/OVERVIEW.md +80 -0
  8. data/README.md +282 -21
  9. data/Rakefile +1 -1
  10. data/bin/hook +127 -10
  11. data/buildnotes.md +29 -0
  12. data/hook.rdoc +63 -4
  13. data/hookapp.gemspec +9 -8
  14. data/html/App.html +119 -0
  15. data/html/GLI.html +99 -0
  16. data/html/GLI/Commands.html +99 -0
  17. data/html/GLI/Commands/Doc.html +99 -0
  18. data/html/GLI/Commands/MarkdownDocumentListener.html +717 -0
  19. data/html/Hook.html +113 -0
  20. data/html/HookApp.html +1222 -0
  21. data/html/Hooker.html +119 -0
  22. data/html/README_rdoc.html +328 -0
  23. data/html/String.html +427 -0
  24. data/html/created.rid +9 -0
  25. data/html/css/fonts.css +167 -0
  26. data/html/css/rdoc.css +619 -0
  27. data/html/fonts/Lato-Light.ttf +0 -0
  28. data/html/fonts/Lato-LightItalic.ttf +0 -0
  29. data/html/fonts/Lato-Regular.ttf +0 -0
  30. data/html/fonts/Lato-RegularItalic.ttf +0 -0
  31. data/html/fonts/SourceCodePro-Bold.ttf +0 -0
  32. data/html/fonts/SourceCodePro-Regular.ttf +0 -0
  33. data/html/images/add.png +0 -0
  34. data/html/images/arrow_up.png +0 -0
  35. data/html/images/brick.png +0 -0
  36. data/html/images/brick_link.png +0 -0
  37. data/html/images/bug.png +0 -0
  38. data/html/images/bullet_black.png +0 -0
  39. data/html/images/bullet_toggle_minus.png +0 -0
  40. data/html/images/bullet_toggle_plus.png +0 -0
  41. data/html/images/date.png +0 -0
  42. data/html/images/delete.png +0 -0
  43. data/html/images/find.png +0 -0
  44. data/html/images/loadingAnimation.gif +0 -0
  45. data/html/images/macFFBgHack.png +0 -0
  46. data/html/images/package.png +0 -0
  47. data/html/images/page_green.png +0 -0
  48. data/html/images/page_white_text.png +0 -0
  49. data/html/images/page_white_width.png +0 -0
  50. data/html/images/plugin.png +0 -0
  51. data/html/images/ruby.png +0 -0
  52. data/html/images/tag_blue.png +0 -0
  53. data/html/images/tag_green.png +0 -0
  54. data/html/images/transparent.png +0 -0
  55. data/html/images/wrench.png +0 -0
  56. data/html/images/wrench_orange.png +0 -0
  57. data/html/images/zoom.png +0 -0
  58. data/html/index.html +308 -0
  59. data/html/js/darkfish.js +84 -0
  60. data/html/js/navigation.js +105 -0
  61. data/html/js/navigation.js.gz +0 -0
  62. data/html/js/search.js +110 -0
  63. data/html/js/search_index.js +1 -0
  64. data/html/js/search_index.js.gz +0 -0
  65. data/html/js/searcher.js +229 -0
  66. data/html/js/searcher.js.gz +0 -0
  67. data/html/table_of_contents.html +409 -0
  68. data/lib/completion/hook_completion.bash +22 -0
  69. data/lib/completion/hook_completion.fish +31 -0
  70. data/lib/completion/hook_completion.zsh +22 -0
  71. data/lib/helpers/fuzzyfilefinder +0 -0
  72. data/lib/hook.rb +5 -1
  73. data/lib/hook/hookapp.rb +489 -0
  74. data/lib/hook/hooker.rb +1 -344
  75. data/lib/hook/markdown_document_listener.rb +164 -0
  76. data/lib/hook/string.rb +60 -0
  77. data/lib/hook/version.rb +3 -1
  78. metadata +76 -12
@@ -1,349 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # String helpers
4
- class String
5
- def split_hook
6
- elements = split(/\|\|/)
7
- {
8
- name: elements[0],
9
- url: elements[1],
10
- path: elements[2]
11
- }
12
- end
13
-
14
- def split_hooks
15
- split(/\^\^/).map(&:split_hook)
16
- end
17
-
18
- def valid_hook
19
- if File.exist?(self)
20
- File.expand_path(self)
21
- elsif self =~ /^\[.*?\]\((.*?)\)$/
22
- mdlink = $1
23
- mdlink.valid_hook
24
- else
25
- self
26
- end
27
- end
28
-
29
- def valid_hook!
30
- replace valid_hook
31
- end
32
-
33
- # Capitalize only if no uppercase
34
- def cap
35
- unless self =~ /[A-Z]/
36
- capitalize
37
- else
38
- self
39
- end
40
- end
41
-
42
- def cap!
43
- replace cap
44
- end
45
-
46
- def clip
47
- res = `/bin/echo -n #{Shellwords.escape(self)} | pbcopy`.strip
48
- raise "Failed to copy to clipboard" unless res.empty?
49
-
50
- true
51
- end
52
- end
53
-
54
3
  # Hook.app CLI interface
55
4
  class Hooker
56
- def initialize(global_args)
57
- @global_args = global_args
58
- end
59
-
60
- def validate_format(fmt, options)
61
- valid_format_rx = options.map { |format| format.sub(/^(.)(.*)$/, '^\1(\2)?$') }
62
- valid_format = false
63
- valid_format_rx.each_with_index do |rx, i|
64
- cmp = Regexp.new(rx, 'i')
65
- next unless fmt =~ cmp
66
-
67
- valid_format = options[i]
68
- break
69
- end
70
- valid_format
71
- end
72
-
73
- def bookmark_for(url)
74
- url.valid_hook!
75
- raise "Invalid target: #{url}" unless url
76
-
77
- mark = `osascript <<'APPLESCRIPT'
78
- tell application "Hook"
79
- set _hook to bookmark from URL "#{url}"
80
- return title of _hook & "||" & address of _hook & "||" & path of _hook
81
- end tell
82
- APPLESCRIPT`.strip
83
- mark.split_hook
84
- end
85
-
86
- def get_hooks(url)
87
- url.valid_hook!
88
- raise "Invalid target: #{url}" unless url
89
-
90
- hooks = `osascript <<'APPLESCRIPT'
91
- tell application "Hook"
92
- set _mark to bookmark from URL "#{url}"
93
- if _mark is {} then return ""
94
- set _hooks to bookmarks hooked to _mark
95
- set _out to {}
96
- repeat with _hook in _hooks
97
- set _out to _out & (title of _hook & "||" & address of _hook & "||" & path of _hook)
98
- end repeat
99
- set {astid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "^^"}
100
- set _output to _out as string
101
- set AppleScript's text item delimiters to astid
102
- return _output
103
- end tell
104
- APPLESCRIPT`.strip
105
- hooks.split_hooks
106
- end
107
-
108
- def bookmark_from_app(app, opts)
109
- mark = `osascript <<'APPLESCRIPT'
110
- tell application "System Events" to set front_app to name of first application process whose frontmost is true
111
- tell application "#{app}" to activate
112
- delay 2
113
- tell application "Hook"
114
- set _hook to (bookmark from active window)
115
- set _output to (title of _hook & "||" & address of _hook & "||" & path of _hook)
116
- end tell
117
- tell application front_app to activate
118
- return _output
119
- APPLESCRIPT`.strip.split_hook
120
- title = mark[:name].empty? ? "#{app.cap} link" : mark[:name]
121
- output = opts[:markdown] ? "[#{title}](#{mark[:url]})" : mark[:url]
122
-
123
- if opts[:copy]
124
- "Copied Markdown link for '#{title}' to clipboard" if output.clip
125
- else
126
- output
127
- end
128
- end
129
-
130
- def clip_bookmark(url, opts)
131
- mark = bookmark_for(url)
132
- copy_bookmark(mark[:name], mark[:url], opts)
133
- end
134
-
135
- def copy_bookmark(title, url, opts)
136
- raise "No URL found" if url.empty?
137
- title = title.empty? ? 'No title' : title
138
- output = opts[:markdown] ? "[#{title}](#{url})" : url
139
- output.clip
140
- %(Copied #{opts[:markdown] ? 'Markdown link' : 'Hook URL'} for '#{title}' to clipboard)
141
- end
142
-
143
- def select_hook(marks)
144
- intpad = marks.length.to_s.length + 1
145
- marks.each_with_index do |mark, i|
146
- STDERR.printf "%#{intpad}d) %s\n", i + 1, mark[:name]
147
- end
148
- STDERR.print 'Open which bookmark: '
149
- sel = STDIN.gets.strip.to_i
150
- raise 'Invalid selection' unless sel.positive? && sel <= marks.length
151
-
152
- marks[sel - 1]
153
- end
154
-
155
- def open_gui(url)
156
- `osascript <<'APPLESCRIPT'
157
- tell application "Hook"
158
- set _mark to bookmark from URL "#{url.valid_hook}"
159
- invoke on _mark
160
- end tell
161
- APPLESCRIPT`
162
- end
163
-
164
- def open_linked(url)
165
- marks = get_hooks(url)
166
- if marks.empty?
167
- warn "No hooks found for #{url}"
168
- else
169
- res = select_hook(marks)
170
- `open '#{res[:url]}'`
171
- end
172
- end
173
-
174
- def link_files(args)
175
- target = args.pop
176
- target.valid_hook!
177
- raise "Invalid target: #{target}" unless target
178
-
179
- args.each do |file|
180
- file.valid_hook!
181
- raise "Invalid target: #{file}" unless file
182
-
183
- puts "Linking #{file} and #{target}..."
184
- `osascript <<'APPLESCRIPT'
185
- tell application "Hook"
186
- set _mark1 to bookmark from URL "#{file}"
187
- set _mark2 to bookmark from URL "#{target}"
188
- hook together _mark1 and _mark2
189
- return true
190
- end tell
191
- APPLESCRIPT`
192
- end
193
- "Linked #{args.length} files to #{target}"
194
- end
195
-
196
- def clone_hooks(args)
197
- target = args.pop.valid_hook
198
- source = args[0].valid_hook
199
-
200
- if target && source
201
- hooks = get_hooks(source)
202
- hooks.each do |hook|
203
- `osascript <<'APPLESCRIPT'
204
- tell application "Hook"
205
- set _mark1 to bookmark from URL "#{hook[:url]}"
206
- set _mark2 to bookmark from URL "#{target}"
207
- hook together _mark1 and _mark2
208
- return true
209
- end tell
210
- APPLESCRIPT`
211
- end
212
- "Hooks from #{source} cloned to #{target}"
213
- else
214
- raise 'Invalid file specified'
215
- end
216
- end
217
-
218
- def delete_all_hooks(url)
219
- STDERR.print "Are you sure you want to delete ALL hooks from #{url} (y/N)? "
220
- res = STDIN.gets.strip
221
- if res =~ /^y/i
222
- get_hooks(url).each do |hook|
223
- `osascript <<'APPLESCRIPT'
224
- tell application "Hook"
225
- set _mark1 to bookmark from URL "#{hook[:url]}"
226
- set _mark2 to bookmark from URL "#{url}"
227
- remove hook between _mark1 and _mark2
228
- return true
229
- end tell
230
- APPLESCRIPT`
231
- end
232
- "Removed all hooks from #{url}"
233
- end
234
- end
235
-
236
- def delete_hooks(args, opts)
237
- urls = args.map(&:valid_hook).delete_if { |url| !url }
238
- output = []
239
- if opts[:all]
240
- urls.each_with_index do |url, i|
241
- raise "Invalid target: #{args[i]}" unless url
242
-
243
- output.push(delete_all_hooks(url))
244
- end
245
- return output.join("\n")
246
- end
247
-
248
- if urls.length == 2
249
- source = urls[0]
250
- target = urls[1]
251
- `osascript <<'APPLESCRIPT'
252
- tell application "Hook"
253
- set _mark1 to bookmark from URL "#{source}"
254
- set _mark2 to bookmark from URL "#{target}"
255
- remove hook between _mark1 and _mark2
256
- return true
257
- end tell
258
- APPLESCRIPT`
259
- "Hook removed between #{source} and #{target}"
260
- else
261
- raise 'Invalid number of URLs or files specified'
262
-
263
- end
264
- end
265
-
266
- def link_all(args)
267
- args.each do |file|
268
- source = file.valid_hook
269
- link_to = args.dup.map(&:valid_hook).reject { |url| url == source }
270
- link_to.each do |url|
271
- `osascript <<'APPLESCRIPT'
272
- tell application "Hook"
273
- set _mark1 to bookmark from URL "#{source}"
274
- set _mark2 to bookmark from URL "#{url}"
275
- hook together _mark1 and _mark2
276
- return true
277
- end tell
278
- APPLESCRIPT`
279
- end
280
- end
281
- "Linked #{args.length} files to each other"
282
- end
283
-
284
- def linked_bookmarks(args, opts)
285
- result = []
286
-
287
- separator = args.length == 1 && opts[:format] == 'paths' && opts[:null_separator] ? "\0" : "\n"
288
-
289
- args.each do |url|
290
- source_mark = bookmark_for(url)
291
- filename = source_mark[:name]
292
-
293
- case opts[:format]
294
- when /^m/
295
- filename = "[#{source_mark[:name]}](#{source_mark[:url]})"
296
- filename += " <file://#{CGI.escape(source_mark[:path])}>" if source_mark[:path]
297
- when /^p/
298
- filename = "File: #{source_mark[:name]}"
299
- filename += " (#{source_mark[:path]})" if source_mark[:path]
300
- when /^h/
301
- filename = "File: #{source_mark[:name]}"
302
- filename += " (#{source_mark[:url]})" if source_mark[:url]
303
- else
304
- filename = "Bookmarks attached to #{source_mark[:path] || source_mark[:url]}"
305
- end
306
-
307
- hooks_arr = get_hooks(url)
308
-
309
- if !hooks_arr.empty?
310
- hooks_arr.reject! { |h| h[:path].nil? || h[:path] == '' } if opts[:files_only]
311
-
312
- output = []
313
-
314
- case opts[:format]
315
- when /^m/
316
- hooks_arr.each do |h|
317
- output.push("- [#{h[:name]}](#{h[:url]})")
318
- end
319
- when /^p/
320
- hooks_arr.each do |h|
321
- output.push(h[:path].nil? ? h[:url] : h[:path])
322
- end
323
- when /^h/
324
- hooks_arr.each do |h|
325
- output.push(h[:url])
326
- end
327
- else
328
- hooks_arr.each do |h|
329
- output.push("Title: #{h[:name]}\nPath: #{h[:path]}\nAddress: #{h[:url]}\n---------------------")
330
- end
331
- end
332
- else
333
- output = ['No bookmarks']
334
- end
335
- result.push({ file: filename, links: output.join(separator) })
336
- end
337
-
338
- if result.length > 1 || opts[:format] == 'verbose'
339
- result.map! do |res|
340
- "#{res[:file]}\n\n#{res[:links]}\n"
341
- end
342
- else
343
- result.map! do |res|
344
- res[:links]
345
- end
346
- end
347
- result.join(separator)
348
- end
5
+ include HookApp
349
6
  end
@@ -0,0 +1,164 @@
1
+ require 'stringio'
2
+ require 'time'
3
+ require 'fileutils'
4
+
5
+ module GLI
6
+ module Commands
7
+ # DocumentListener class for GLI documentation generator
8
+ class MarkdownDocumentListener
9
+ def initialize(_global_options, _options, _arguments, app)
10
+ @exe = app.exe_name
11
+ if File.exist?('README.md') # Back up existing README
12
+ FileUtils.mv('README.md', 'README.bak')
13
+ $stderr.puts "Backing up existing README.md to README.bak"
14
+ end
15
+ @io = File.new('README.md', 'w')
16
+ @nest = '#'
17
+ @arg_name_formatter = GLI::Commands::HelpModules::ArgNameFormatter.new
18
+ end
19
+
20
+ def beginning
21
+ end
22
+
23
+ # Called when processing has completed
24
+ def ending
25
+ if File.exist?('CREDITS.md')
26
+ @io.puts IO.read('CREDITS.md')
27
+ @io.puts
28
+ end
29
+ if File.exist?('LICENSE.md')
30
+ @io.puts IO.read('LICENSE.md')
31
+ @io.puts
32
+ end
33
+ @io.puts
34
+ @io.puts "Documentation generated #{Time.now.strftime('%Y-%m-%d %H:%M')}"
35
+ @io.puts
36
+ @io.close
37
+ end
38
+
39
+ # Gives you the program description
40
+ def program_desc(desc)
41
+ @io.puts "# #{@exe} CLI"
42
+ @io.puts
43
+ @io.puts desc
44
+ @io.puts
45
+ end
46
+
47
+ def program_long_desc(desc)
48
+ @io.puts "> #{desc}"
49
+ @io.puts
50
+ end
51
+
52
+ # Gives you the program version
53
+ def version(version)
54
+ @io.puts "*v#{version}*"
55
+ @io.puts
56
+ # Hacking in the overview file
57
+ if File.exist?('OVERVIEW.md')
58
+ @io.puts IO.read('OVERVIEW.md')
59
+ @io.puts
60
+ end
61
+ end
62
+
63
+ def options
64
+ if @nest.size == 1
65
+ @io.puts "## Global Options"
66
+ else
67
+ @io.puts header("Options", 1)
68
+ end
69
+ @io.puts
70
+ end
71
+
72
+ # Gives you a flag in the current context
73
+ def flag(name, aliases, desc, long_desc, default_value, arg_name, must_match, _type)
74
+ invocations = ([name] + Array(aliases)).map { |_| "`" + add_dashes(_) + "`" }.join(' | ')
75
+ usage = "#{invocations} #{arg_name || 'arg'}"
76
+ @io.puts header(usage, 2)
77
+ @io.puts
78
+ @io.puts String(desc).strip
79
+ @io.puts "\n*Default Value:* `#{default_value || 'None'}`\n" unless default_value.nil?
80
+ @io.puts "\n*Must Match:* `#{must_match.to_s}`\n" unless must_match.nil?
81
+ cmd_desc = String(long_desc).strip
82
+ @io.puts "> #{cmd_desc}\n" unless cmd_desc.length == 0
83
+ @io.puts
84
+ end
85
+
86
+ # Gives you a switch in the current context
87
+ def switch(name, aliases, desc, long_desc, negatable)
88
+ if negatable
89
+ name = "[no-]#{name}" if name.to_s.length > 1
90
+ aliases = aliases.map { |_| _.to_s.length > 1 ? "[no-]#{_}" : _ }
91
+ end
92
+ invocations = ([name] + aliases).map { |_| "`" + add_dashes(_) + "`" }.join('|')
93
+ @io.puts header("#{invocations}", 2)
94
+ @io.puts
95
+ @io.puts String(desc).strip
96
+ cmd_desc = String(long_desc).strip
97
+ @io.puts "\n> #{cmd_desc}\n" unless cmd_desc.length == 0
98
+ @io.puts
99
+ end
100
+
101
+ def end_options
102
+ end
103
+
104
+ def commands
105
+ @io.puts header("Commands", 1)
106
+ @io.puts
107
+ increment_nest
108
+ end
109
+
110
+ # Gives you a command in the current context and creates a new context of this command
111
+ def command(name, aliases, desc, long_desc, arg_name, arg_options)
112
+ arg_name_fmt = @arg_name_formatter.format(arg_name, arg_options, [])
113
+ @io.puts header("`$ #{@exe}` <mark>`#{([name] + aliases).join('|')}`</mark> `#{arg_name_fmt}`", 1)
114
+ @io.puts
115
+ @io.puts "*#{String(desc).strip}*"
116
+ @io.puts
117
+ cmd_desc = String(long_desc).strip.split("\n").map { |_| "> #{_}" }.join("\n")
118
+ @io.puts "#{cmd_desc}\n\n" unless cmd_desc.length == 0
119
+ increment_nest
120
+ end
121
+
122
+ # Ends a command, and "pops" you back up one context
123
+ def end_command(_name)
124
+ decrement_nest
125
+ @io.puts "* * * * * *\n\n" unless @nest.size > 2
126
+ end
127
+
128
+ # Gives you the name of the current command in the current context
129
+ def default_command(name)
130
+ @io.puts "#### [Default Command] #{name}" unless name.nil?
131
+ end
132
+
133
+ def end_commands
134
+ decrement_nest
135
+ end
136
+
137
+ private
138
+
139
+ def add_dashes(name)
140
+ name = "-#{name}"
141
+ name = "-#{name}" if name.length > 2
142
+ name
143
+ end
144
+
145
+ def header(content, increment)
146
+ if @nest.size + increment > 6
147
+ "**#{content}**"
148
+ else
149
+ "#{@nest}#{'#'*increment} #{content}"
150
+ end
151
+ end
152
+
153
+ def increment_nest(increment=1)
154
+ @nest = "#{@nest}#{'#'*increment}"
155
+ end
156
+
157
+ def decrement_nest(increment=1)
158
+ @nest.gsub!(/#{'#'*increment}$/, '')
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ GLI::Commands::Doc::FORMATS['markdown'] = GLI::Commands::MarkdownDocumentListener