hookapp 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 69d899b79ca15a4966ce811c6bc083f93a1152f4a6c70611c090dbfc01d6003b
4
+ data.tar.gz: '066122764283adadf0a9929659d0f9f87b136c2f3b65312a6afda34606aaa5d9'
5
+ SHA512:
6
+ metadata.gz: bebe816553c3b5783a5628ea4e7140f742decc5cb394e64b4d13643ba0493016c0fb9c75b98fecc9d5b069f238e22f8e9b627c282d64d4f5566c1bb39df3a7e4
7
+ data.tar.gz: 01114bbe9cfb43a1d0d7573c51d041a8027bc1d98e21c96638fd3ce6fd25919d26ae0cdcab121671c09b82be2eb77db2cf462a762f9aaa6350b9a784dbc78b78
@@ -0,0 +1,2 @@
1
+ *.test
2
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,61 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hookapp (0.0.2)
5
+ gli (= 2.19.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ aruba (0.14.14)
11
+ childprocess (>= 0.6.3, < 4.0.0)
12
+ contracts (~> 0.9)
13
+ cucumber (>= 1.3.19)
14
+ ffi (~> 1.9)
15
+ rspec-expectations (>= 2.99)
16
+ thor (>= 0.19, < 2.0)
17
+ backports (3.17.0)
18
+ builder (3.2.4)
19
+ childprocess (3.0.0)
20
+ contracts (0.16.0)
21
+ cucumber (3.1.2)
22
+ builder (>= 2.1.2)
23
+ cucumber-core (~> 3.2.0)
24
+ cucumber-expressions (~> 6.0.1)
25
+ cucumber-wire (~> 0.0.1)
26
+ diff-lcs (~> 1.3)
27
+ gherkin (~> 5.1.0)
28
+ multi_json (>= 1.7.5, < 2.0)
29
+ multi_test (>= 0.1.2)
30
+ cucumber-core (3.2.1)
31
+ backports (>= 3.8.0)
32
+ cucumber-tag_expressions (~> 1.1.0)
33
+ gherkin (~> 5.0)
34
+ cucumber-expressions (6.0.1)
35
+ cucumber-tag_expressions (1.1.1)
36
+ cucumber-wire (0.0.1)
37
+ diff-lcs (1.3)
38
+ ffi (1.12.2)
39
+ gherkin (5.1.0)
40
+ gli (2.19.0)
41
+ multi_json (1.14.1)
42
+ multi_test (0.1.2)
43
+ rake (13.0.1)
44
+ rdoc (6.2.0)
45
+ rspec-expectations (3.9.0)
46
+ diff-lcs (>= 1.2.0, < 2.0)
47
+ rspec-support (~> 3.9.0)
48
+ rspec-support (3.9.2)
49
+ thor (1.0.1)
50
+
51
+ PLATFORMS
52
+ ruby
53
+
54
+ DEPENDENCIES
55
+ aruba
56
+ hookapp!
57
+ rake
58
+ rdoc
59
+
60
+ BUNDLED WITH
61
+ 2.1.4
@@ -0,0 +1,48 @@
1
+ # hookapp
2
+
3
+ A CLI for Hook.app on macOS.
4
+
5
+ ## Installation
6
+
7
+ `gem install hookapp`
8
+
9
+ ## Usage
10
+
11
+ Run `hook help` for usage:
12
+
13
+ NAME
14
+ hook - CLI interface for Hook.app (macOS)
15
+
16
+ SYNOPSIS
17
+ hook [global options] command [command options] [arguments...]
18
+
19
+ VERSION
20
+ 0.0.1
21
+
22
+ GLOBAL OPTIONS
23
+ --help - Show this message
24
+ --version - Display the program version
25
+
26
+ COMMANDS
27
+ clip - Copy Hook URL for file/url to clipboard
28
+ clone, cp - Clone all hooks from one file or url onto another
29
+ help - Shows a list of commands or help for one command
30
+ link, ln - Create bidirectional hooks between two or more files/urls
31
+ list, ls - List hooks on a file or url
32
+ open, gui - Open the specified file or url in Hook GUI
33
+ remove, rm - Remove a hook between two files/urls
34
+ select - Select from hooks on a file/url and open in default application
35
+
36
+ Run `hook help COMMAND` on any of the commands for more details.
37
+
38
+ ## Basics
39
+
40
+ ### Creating hooks
41
+
42
+ The `link` command always creates bidirectional hooks. Give it two or more files or urls and it will create links between them. By default it links all files to the last file in the list. Add the `--all` flag to link all files to every other file in the list. This can be abbreviated as `hook ln -a file1.md file2.pdf "https://brettterpstra.com"`.
43
+
44
+ ### Listing and opening hooked files/URLs
45
+
46
+ Use the `list` (aliased as `ls`) command to list all attached hooks for a file. With no output format specified, `ls` will show the paths to all hooks, or the hook url if the hook doesn't have a file path (e.g. a URL). You can specify an output format using `--output_format` (abbreviated as `-o`) with one of "markdown", "hooks", "paths", or "verbose". Formats can be abbreviated to their first letter, so to display a Markdown list of all hooked files, use `hook ls -o m file1.md`.
47
+
48
+ You can select a hook from a menu using `hook select file1.md`, and the resulting selection will be opened in the default application for its filetype.
@@ -0,0 +1,6 @@
1
+ = hookapp
2
+
3
+ A CLI for Hook.app on macOS.
4
+
5
+ :include:hook.rdoc
6
+
@@ -0,0 +1,44 @@
1
+ require 'rake/clean'
2
+ require 'rubygems'
3
+ require 'rubygems/package_task'
4
+ require 'rdoc/task'
5
+ require 'cucumber'
6
+ require 'cucumber/rake/task'
7
+ Rake::RDocTask.new do |rd|
8
+ rd.main = "README.rdoc"
9
+ rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
10
+ rd.title = 'Your application title'
11
+ end
12
+
13
+ spec = eval(File.read('hookapp.gemspec'))
14
+
15
+ Gem::PackageTask.new(spec) do |pkg|
16
+ end
17
+ CUKE_RESULTS = 'results.html'
18
+ CLEAN << CUKE_RESULTS
19
+ desc 'Run features'
20
+ Cucumber::Rake::Task.new(:features) do |t|
21
+ opts = "features --format html -o #{CUKE_RESULTS} --format progress -x"
22
+ opts += " --tags #{ENV['TAGS']}" if ENV['TAGS']
23
+ t.cucumber_opts = opts
24
+ t.fork = false
25
+ end
26
+
27
+ desc 'Run features tagged as work-in-progress (@wip)'
28
+ Cucumber::Rake::Task.new('features:wip') do |t|
29
+ tag_opts = ' --tags ~@pending'
30
+ tag_opts = ' --tags @wip'
31
+ t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty -x -s#{tag_opts}"
32
+ t.fork = false
33
+ end
34
+
35
+ task :cucumber => :features
36
+ task 'cucumber:wip' => 'features:wip'
37
+ task :wip => 'features:wip'
38
+ require 'rake/testtask'
39
+ Rake::TestTask.new do |t|
40
+ t.libs << "test"
41
+ t.test_files = FileList['test/*_test.rb']
42
+ end
43
+
44
+ task :default => [:test,:features]
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'gli'
5
+ require 'hook'
6
+
7
+ # Main class for GLI app
8
+ class App
9
+ extend GLI::App
10
+
11
+ program_desc 'CLI interface for Hook.app (macOS)'
12
+
13
+ version Hook::VERSION
14
+
15
+ subcommand_option_handling :normal
16
+ arguments :strict
17
+
18
+ hooker = nil
19
+
20
+ # desc 'Describe some switch here'
21
+ # switch [:s,:switch]
22
+
23
+ # desc 'Describe some flag here'
24
+ # default_value 'the default'
25
+ # arg_name 'The name of the argument'
26
+ # flag [:f,:flagname]
27
+
28
+ desc 'List hooks on a file or url'
29
+ arg_name 'FILE_OR_URL [FILE_OR_URL, ...]'
30
+ command %i[list ls] do |c|
31
+ c.desc 'Output only bookmarks with file paths (exclude e.g. emails)'
32
+ c.switch %i[f files_only], { negatable: false, default_value: false }
33
+
34
+ c.desc 'Separate results with NULL separator, only applies with "paths" output for single file argument'
35
+ c.switch %i[null], { negatable: false, default_value: false }
36
+
37
+ valid_formats = %w[hooks paths markdown verbose]
38
+ fmt_list = valid_formats.map { |fmt| fmt.sub(/^(.)(.*?)$/, '(\1)\2') }.join(', ')
39
+ c.desc "Output format [#{fmt_list}]"
40
+ c.flag %i[o output_format], { arg_name: 'format', default_value: 'paths' }
41
+
42
+ c.action do |_global_options, options, args|
43
+ valid_format = hooker.validate_format(options[:o], valid_formats)
44
+ raise 'Invalid output format' unless valid_format
45
+
46
+ result = hooker.linked_bookmarks(args, { files_only: options[:f],
47
+ format: valid_format,
48
+ null_separator: options[:null] })
49
+
50
+ puts result
51
+ end
52
+ end
53
+
54
+ desc 'Create bidirectional hooks between two or more files/urls'
55
+ arg_name 'SOURCE [SOURCE, ...] TARGET'
56
+ command %i[link ln] do |c|
57
+ c.desc 'Link every listed file or url to every other'
58
+ c.switch %i[a all], { negatable: false, default_value: false }
59
+
60
+ c.desc 'Paste URL from clipboard'
61
+ c.switch %i[p paste], { negatable: false, default_value: false }
62
+
63
+ c.action do |_global_options, options, args|
64
+ if options[:p]
65
+ clipboard = `pbpaste`.strip
66
+ clipboard.valid_hook!
67
+ args.push(clipboard) if clipboard
68
+ end
69
+ if args.length < 2
70
+ raise 'At least 2 files must be specified, or one file with --paste'
71
+ end
72
+ if options[:a]
73
+ puts hooker.link_all(args)
74
+ else
75
+ puts hooker.link_files(args)
76
+ end
77
+ end
78
+ end
79
+
80
+ desc 'Copy Hook URL for file/url to clipboard'
81
+ arg_name 'FILE_OR_URL'
82
+ command %i[clip] do |c|
83
+ c.desc 'Copy as Markdown'
84
+ c.switch %i[m markdown]
85
+
86
+ c.action do |_global_options, options, args|
87
+ result = hooker.copy_bookmark(args[0], { markdown: options[:m] })
88
+ puts result
89
+ end
90
+ end
91
+
92
+ desc 'Remove a hook between two files/urls'
93
+ arg_name 'ITEM_1 ITEM_2'
94
+ command %i[remove rm] do |c|
95
+ c.desc 'Remove ALL links on files, requires confirmation'
96
+ c.switch %i[a all]
97
+
98
+ c.action do |_global_options, options, args|
99
+ result = hooker.delete_hooks(args, { all: options[:r] })
100
+ puts result
101
+ end
102
+ end
103
+
104
+ desc 'Clone all hooks from one file or url onto another'
105
+ arg_name 'SOURCE TARGET'
106
+ command %i[clone cp] do |c|
107
+ c.action do |_global_options, _options, args|
108
+ raise "Wrong number of arguments. Two file paths or urls required (#{args.length} given)" if args.length != 2
109
+
110
+ result = hooker.clone_hooks(args)
111
+ puts result
112
+ end
113
+ end
114
+
115
+ desc 'Select from hooks on a file/url and open in default application'
116
+ arg_name 'FILE_OR_URL'
117
+ command %i[select] do |c|
118
+ c.action do |_global_options, _options, args|
119
+ raise "Wrong number of arguments. One file path or url required (#{args.length} given)" if args.length != 1
120
+
121
+ hooker.open_linked(args[0])
122
+ end
123
+ end
124
+
125
+ desc 'Open the specified file or url in Hook GUI'
126
+ arg_name 'FILE_OR_URL'
127
+ command %i[open gui] do |c|
128
+ c.action do |_global_options, _options, args|
129
+ raise "Wrong number of arguments. One file path or url required (#{args.length} given)" if args.length != 1
130
+
131
+ hooker.open_gui(args[0])
132
+ end
133
+ end
134
+
135
+ pre do |global, _command, _options, _args|
136
+ # Pre logic here
137
+ # Return true to proceed; false to abort and not call the
138
+ # chosen command
139
+ # Use skips_pre before a command to skip this block
140
+ # on that command only
141
+ hooker = Hooker.new(global)
142
+ true
143
+ end
144
+
145
+ post do |_global, _command, _options, _args|
146
+ # Post logic here
147
+ # Use skips_post before a command to skip this
148
+ # block on that command only
149
+ end
150
+
151
+ on_error do |exception|
152
+ # Error logic here
153
+ # return false to skip default error handling
154
+ true
155
+ end
156
+ end
157
+
158
+ exit App.run(ARGV)
@@ -0,0 +1,8 @@
1
+ Feature: My bootstrapped app kinda works
2
+ In order to get going on coding my awesome app
3
+ I want to have aruba and cucumber setup
4
+ So I don't have to do it myself
5
+
6
+ Scenario: App just runs
7
+ When I get help for "hook"
8
+ Then the exit status should be 0
@@ -0,0 +1,6 @@
1
+ When /^I get help for "([^"]*)"$/ do |app_name|
2
+ @app_name = app_name
3
+ step %(I run `#{app_name} help`)
4
+ end
5
+
6
+ # Add more step definitions here
@@ -0,0 +1,15 @@
1
+ require 'aruba/cucumber'
2
+
3
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
4
+ LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
5
+
6
+ Before do
7
+ # Using "announce" causes massive warnings on 1.9.2
8
+ @puts = true
9
+ @original_rubylib = ENV['RUBYLIB']
10
+ ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
11
+ end
12
+
13
+ After do
14
+ ENV['RUBYLIB'] = @original_rubylib
15
+ end
@@ -0,0 +1,95 @@
1
+ == hook - CLI interface for Hook.app (macOS)
2
+
3
+ v0.0.2
4
+
5
+ === Global Options
6
+ === --help
7
+ Show this message
8
+
9
+
10
+
11
+ === --version
12
+ Display the program version
13
+
14
+
15
+
16
+ === Commands
17
+ ==== Command: <tt>clip FILE_OR_URL</tt>
18
+ Copy Hook URL for file/url to clipboard
19
+
20
+
21
+ ===== Options
22
+ ===== -m|--[no-]markdown
23
+ Copy as Markdown
24
+
25
+
26
+
27
+ ==== Command: <tt>clone|cp SOURCE TARGET</tt>
28
+ Clone all hooks from one file or url onto another
29
+
30
+
31
+ ==== Command: <tt>help command</tt>
32
+ Shows a list of commands or help for one command
33
+
34
+ Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function
35
+ ===== Options
36
+ ===== -c
37
+ List commands one per line, to assist with shell completion
38
+
39
+
40
+
41
+ ==== Command: <tt>link|ln SOURCE [SOURCE, ...] TARGET</tt>
42
+ Create bidirectional hooks between two or more files/urls
43
+
44
+
45
+ ===== Options
46
+ ===== -a|--all
47
+ Link every listed file or url to every other
48
+
49
+
50
+
51
+ ===== -p|--paste
52
+ Paste URL from clipboard
53
+
54
+
55
+
56
+ ==== Command: <tt>list|ls FILE_OR_URL [FILE_OR_URL, ...]</tt>
57
+ List hooks on a file or url
58
+
59
+
60
+ ===== Options
61
+ ===== -o|--output_format format
62
+
63
+ Output format [(h)ooks, (p)aths, (m)arkdown, (v)erbose]
64
+
65
+ [Default Value] paths
66
+
67
+
68
+ ===== -f|--files_only
69
+ Output only bookmarks with file paths (exclude e.g. emails)
70
+
71
+
72
+
73
+ ===== --null
74
+ Separate results with NULL separator, only applies with "paths" output for single file argument
75
+
76
+
77
+
78
+ ==== Command: <tt>open|gui FILE_OR_URL</tt>
79
+ Open the specified file or url in Hook GUI
80
+
81
+
82
+ ==== Command: <tt>remove|rm ITEM_1 ITEM_2</tt>
83
+ Remove a hook between two files/urls
84
+
85
+
86
+ ===== Options
87
+ ===== -a|--[no-]all
88
+ Remove ALL links on files, requires confirmation
89
+
90
+
91
+
92
+ ==== Command: <tt>select FILE_OR_URL</tt>
93
+ Select from hooks on a file/url and open in default application
94
+
95
+
@@ -0,0 +1,22 @@
1
+ # Ensure we require the local version and not one we might have installed already
2
+ require File.join([File.dirname(__FILE__), 'lib', 'hook', 'version.rb'])
3
+ spec = Gem::Specification.new do |s|
4
+ s.name = 'hookapp'
5
+ s.version = Hook::VERSION
6
+ s.author = 'Brett Terpstra'
7
+ s.email = 'me@brettterpstra.com'
8
+ s.homepage = 'https://brettterpstra.com'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.summary = 'A CLI for Hook.app (macOS)'
11
+ s.files = `git ls-files`.split("
12
+ ")
13
+ s.require_paths << 'lib'
14
+ s.extra_rdoc_files = ['README.rdoc','hook.rdoc']
15
+ s.rdoc_options << '--title' << 'hook' << '--main' << 'README.rdoc' << '-ri'
16
+ s.bindir = 'bin'
17
+ s.executables << 'hook'
18
+ s.add_development_dependency('rake')
19
+ s.add_development_dependency('rdoc')
20
+ s.add_development_dependency('aruba')
21
+ s.add_runtime_dependency('gli','2.19.0')
22
+ end
@@ -0,0 +1,5 @@
1
+ require 'hook/version.rb'
2
+
3
+ require 'shellwords'
4
+ require 'cgi'
5
+ require 'hook/hooker.rb'
@@ -0,0 +1,313 @@
1
+ # frozen_string_literal: true
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 =~ /^(hook|http)/
22
+ self
23
+ else
24
+ if self =~ /^\[.*?\]\((.*?)\)$/
25
+ mdlink = $1
26
+ mdlink.valid_hook
27
+ else
28
+ false
29
+ end
30
+ end
31
+ end
32
+
33
+ def valid_hook!
34
+ replace valid_hook
35
+ end
36
+ end
37
+
38
+ # Hook.app CLI interface
39
+ class Hooker
40
+ def initialize(global_args)
41
+ @global_args = global_args
42
+ end
43
+
44
+ def validate_format(fmt, options)
45
+ valid_format_rx = options.map { |fmt| fmt.sub(/^(.)(.*)$/, '^\1(\2)?$') }
46
+ valid_format = false
47
+ valid_format_rx.each_with_index do |rx, i|
48
+ cmp = Regexp.new(rx, 'i')
49
+ next unless fmt =~ cmp
50
+ valid_format = options[i]
51
+ break
52
+ end
53
+ return valid_format
54
+ end
55
+
56
+ def bookmark_for(url)
57
+ url.valid_hook!
58
+ raise "Invalid target: #{url}" unless url
59
+
60
+ mark = `osascript <<'APPLESCRIPT'
61
+ tell application "Hook"
62
+ set _hook to bookmark from URL "#{url}"
63
+ return title of _hook & "||" & address of _hook & "||" & path of _hook
64
+ end tell
65
+ APPLESCRIPT`.strip
66
+ mark.split_hook
67
+ end
68
+
69
+ def get_hooks(url)
70
+ url.valid_hook!
71
+ raise "Invalid target: #{url}" unless url
72
+
73
+ hooks = `osascript <<'APPLESCRIPT'
74
+ tell application "Hook"
75
+ set _mark to bookmark from URL "#{url}"
76
+ if _mark is {} then return ""
77
+ set _hooks to bookmarks hooked to _mark
78
+ set _out to {}
79
+ repeat with _hook in _hooks
80
+ set _out to _out & (title of _hook & "||" & address of _hook & "||" & path of _hook)
81
+ end repeat
82
+ set {astid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "^^"}
83
+ set _output to _out as string
84
+ set AppleScript's text item delimiters to astid
85
+ return _output
86
+ end tell
87
+ APPLESCRIPT`.strip
88
+ hooks.split_hooks
89
+ end
90
+
91
+ def copy_bookmark(url, opts)
92
+ mark = bookmark_for(url)
93
+ output = if opts[:markdown]
94
+ "[#{mark[:name]}](#{mark[:url]})"
95
+ else
96
+ mark[:url]
97
+ end
98
+ `/bin/echo -n #{Shellwords.escape(output)} | pbcopy`
99
+ %(Copied #{opts[:markdown] ? 'Markdown link' : 'Hook URL'} for '#{mark[:name]}' to clipboard)
100
+ end
101
+
102
+ def select_hook(marks)
103
+ intpad = marks.length.to_s.length + 1
104
+ marks.each_with_index do |mark, i|
105
+ STDERR.printf "%#{intpad}d) %s\n", i + 1, mark[:name]
106
+ end
107
+ STDERR.print 'Open which bookmark: '
108
+ sel = STDIN.gets.strip.to_i
109
+ if sel.positive? && sel <= marks.length
110
+ marks[sel - 1]
111
+ else
112
+ warn 'Invalid selection'
113
+ Process.exit 1
114
+ end
115
+ end
116
+
117
+ def open_gui(url)
118
+ `osascript <<'APPLESCRIPT'
119
+ tell application "Hook"
120
+ set _mark to bookmark from URL "#{url.valid_hook}"
121
+ invoke on _mark
122
+ end tell
123
+ APPLESCRIPT`
124
+ end
125
+
126
+ def open_linked(url)
127
+ marks = get_hooks(url)
128
+ if marks.empty?
129
+ warn "No hooks found for #{url}"
130
+ else
131
+ res = select_hook(marks)
132
+ `open '#{res[:url]}'`
133
+ end
134
+ end
135
+
136
+ def link_files(args)
137
+ target = args.pop
138
+ target.valid_hook!
139
+ raise "Invalid target: #{target}" unless target
140
+
141
+ args.each do |file|
142
+ file.valid_hook!
143
+ raise "Invalid target: #{file}" unless file
144
+
145
+ puts "Linking #{file} and #{target}..."
146
+ `osascript <<'APPLESCRIPT'
147
+ tell application "Hook"
148
+ set _mark1 to bookmark from URL "#{file}"
149
+ set _mark2 to bookmark from URL "#{target}"
150
+ hook together _mark1 and _mark2
151
+ return true
152
+ end tell
153
+ APPLESCRIPT`
154
+ end
155
+ "Linked #{args.length} files to #{target}"
156
+ end
157
+
158
+ def clone_hooks(args)
159
+ target = args.pop.valid_hook
160
+ source = args[0].valid_hook
161
+
162
+ if target && source
163
+ hooks = get_hooks(source)
164
+ hooks.each do |hook|
165
+ `osascript <<'APPLESCRIPT'
166
+ tell application "Hook"
167
+ set _mark1 to bookmark from URL "#{hook[:url]}"
168
+ set _mark2 to bookmark from URL "#{target}"
169
+ hook together _mark1 and _mark2
170
+ return true
171
+ end tell
172
+ APPLESCRIPT`
173
+ end
174
+ "Hooks from #{source} cloned to #{target}"
175
+ else
176
+ raise 'Invalid file specified'
177
+ end
178
+ end
179
+
180
+ def delete_all_hooks(url)
181
+ STDERR.print "Are you sure you want to delete ALL hooks from #{url} (y/N)? "
182
+ res = STDIN.gets.strip
183
+ if res =~ /^y/i
184
+ get_hooks(url).each do |hook|
185
+ `osascript <<'APPLESCRIPT'
186
+ tell application "Hook"
187
+ set _mark1 to bookmark from URL "#{hook[:url]}"
188
+ set _mark2 to bookmark from URL "#{url}"
189
+ remove hook between _mark1 and _mark2
190
+ return true
191
+ end tell
192
+ APPLESCRIPT`
193
+ end
194
+ "Removed all hooks from #{url}"
195
+ end
196
+ end
197
+
198
+ def delete_hooks(args, opts)
199
+ urls = args.map(&:valid_hook).delete_if { |url| !url }
200
+ output = []
201
+ if opts[:all]
202
+ urls.each_with_index do |url, i|
203
+ raise "Invalid target: #{args[i]}" unless url
204
+
205
+ output.push(delete_all_hooks(url))
206
+ end
207
+ return output.join("\n")
208
+ end
209
+
210
+ if urls.length == 2
211
+ source = urls[0]
212
+ target = urls[1]
213
+ `osascript <<'APPLESCRIPT'
214
+ tell application "Hook"
215
+ set _mark1 to bookmark from URL "#{source}"
216
+ set _mark2 to bookmark from URL "#{target}"
217
+ remove hook between _mark1 and _mark2
218
+ return true
219
+ end tell
220
+ APPLESCRIPT`
221
+ return "Hook removed between #{source} and #{target}"
222
+ else
223
+ raise "Invalid number of URLs or files specified"
224
+
225
+ end
226
+ end
227
+
228
+ def link_all(args)
229
+ args.each do |file|
230
+ source = file.valid_hook
231
+ link_to = args.dup.map(&:valid_url).reject { |url|
232
+ url == source
233
+ }
234
+ link_to.each do |url|
235
+ `osascript <<'APPLESCRIPT'
236
+ tell application "Hook"
237
+ set _mark1 to bookmark from URL "#{source}"
238
+ set _mark2 to bookmark from URL "#{url}"
239
+ hook together _mark1 and _mark2
240
+ return true
241
+ end tell
242
+ APPLESCRIPT`
243
+ end
244
+ end
245
+ "Linked #{args.length} files to each other"
246
+ end
247
+
248
+ def linked_bookmarks(args, opts)
249
+ result = []
250
+
251
+ separator = args.length == 1 && opts[:format] == 'paths' && opts[:null_separator] ? "\0" : "\n"
252
+
253
+ args.each do |url|
254
+ source_mark = bookmark_for(url)
255
+ filename = source_mark[:name]
256
+
257
+ case opts[:format]
258
+ when /^m/
259
+ filename = "[#{source_mark[:name]}](#{source_mark[:url]})"
260
+ filename += " <file://#{CGI.escape(source_mark[:path])}>" if source_mark[:path]
261
+ when /^p/
262
+ filename = "File: #{source_mark[:name]}"
263
+ filename += " (#{source_mark[:path]})" if source_mark[:path]
264
+ when /^h/
265
+ filename = "File: #{source_mark[:name]}"
266
+ filename += " (#{source_mark[:url]})" if source_mark[:url]
267
+ else
268
+ filename = "Bookmarks attached to #{source_mark[:path] || source_mark[:url]}"
269
+ end
270
+
271
+ hooks_arr = get_hooks(url)
272
+
273
+ if !hooks_arr.empty?
274
+ hooks_arr.reject! { |h| h[:path].nil? || h[:path] == '' } if opts[:files_only]
275
+
276
+ output = []
277
+
278
+ case opts[:format]
279
+ when /^m/
280
+ hooks_arr.each do |h|
281
+ output.push("- [#{h[:name]}](#{h[:url]})")
282
+ end
283
+ when /^p/
284
+ hooks_arr.each do |h|
285
+ output.push(h[:path].nil? ? h[:url] : h[:path])
286
+ end
287
+ when /^h/
288
+ hooks_arr.each do |h|
289
+ output.push(h[:url])
290
+ end
291
+ else
292
+ hooks_arr.each do |h|
293
+ output.push("Title: #{h[:name]}\nPath: #{h[:path]}\nAddress: #{h[:url]}\n---------------------")
294
+ end
295
+ end
296
+ else
297
+ output = ['No bookmarks']
298
+ end
299
+ result.push({ file: filename, links: output.join(separator) })
300
+ end
301
+
302
+ if result.length > 1 || opts[:format] == 'verbose'
303
+ result.map! do |res|
304
+ "#{res[:file]}\n\n#{res[:links]}\n"
305
+ end
306
+ else
307
+ result.map! do |res|
308
+ res[:links]
309
+ end
310
+ end
311
+ result.join(separator)
312
+ end
313
+ end
@@ -0,0 +1,3 @@
1
+ module Hook
2
+ VERSION = '0.0.2'
3
+ end
@@ -0,0 +1,14 @@
1
+ require 'test_helper'
2
+
3
+ class DefaultTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ end
7
+
8
+ def teardown
9
+ end
10
+
11
+ def test_the_truth
12
+ assert true
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ require 'test/unit'
2
+
3
+ # Add test libraries you want to use here, e.g. mocha
4
+
5
+ class Test::Unit::TestCase
6
+
7
+ # Add global extensions to the test case class here
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hookapp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Brett Terpstra
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-07-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rdoc
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aruba
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: gli
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.19.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 2.19.0
69
+ description:
70
+ email: me@brettterpstra.com
71
+ executables:
72
+ - hook
73
+ extensions: []
74
+ extra_rdoc_files:
75
+ - README.rdoc
76
+ - hook.rdoc
77
+ files:
78
+ - ".gitignore"
79
+ - Gemfile
80
+ - Gemfile.lock
81
+ - README.md
82
+ - README.rdoc
83
+ - Rakefile
84
+ - bin/hook
85
+ - features/hook.feature
86
+ - features/step_definitions/hook_steps.rb
87
+ - features/support/env.rb
88
+ - hook.rdoc
89
+ - hookapp.gemspec
90
+ - lib/hook.rb
91
+ - lib/hook/hooker.rb
92
+ - lib/hook/version.rb
93
+ - test/default_test.rb
94
+ - test/hookfiles/01.test
95
+ - test/hookfiles/02.test
96
+ - test/hookfiles/03.test
97
+ - test/hookfiles/04.test
98
+ - test/hookfiles/05.test
99
+ - test/hookfiles/06.test
100
+ - test/hookfiles/07.test
101
+ - test/hookfiles/08.test
102
+ - test/hookfiles/09.test
103
+ - test/hookfiles/10.test
104
+ - test/hookfiles/11.test
105
+ - test/hookfiles/12.test
106
+ - test/test_helper.rb
107
+ homepage: https://brettterpstra.com
108
+ licenses: []
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options:
112
+ - "--title"
113
+ - hook
114
+ - "--main"
115
+ - README.rdoc
116
+ - "-ri"
117
+ require_paths:
118
+ - lib
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubygems_version: 3.0.3
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: A CLI for Hook.app (macOS)
135
+ test_files: []