hookapp 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +61 -0
- data/README.md +48 -0
- data/README.rdoc +6 -0
- data/Rakefile +44 -0
- data/bin/hook +158 -0
- data/features/hook.feature +8 -0
- data/features/step_definitions/hook_steps.rb +6 -0
- data/features/support/env.rb +15 -0
- data/hook.rdoc +95 -0
- data/hookapp.gemspec +22 -0
- data/lib/hook.rb +5 -0
- data/lib/hook/hooker.rb +313 -0
- data/lib/hook/version.rb +3 -0
- data/test/default_test.rb +14 -0
- data/test/test_helper.rb +9 -0
- metadata +135 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -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
|
data/README.md
ADDED
@@ -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.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -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]
|
data/bin/hook
ADDED
@@ -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,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
|
data/hook.rdoc
ADDED
@@ -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
|
+
|
data/hookapp.gemspec
ADDED
@@ -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
|
data/lib/hook.rb
ADDED
data/lib/hook/hooker.rb
ADDED
@@ -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
|
data/lib/hook/version.rb
ADDED
data/test/test_helper.rb
ADDED
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: []
|