hookapp 0.0.7 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
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