marked-conductor 1.0.6 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 684cf7a82ae4cce7240617f832ce52572b0373e578aaff487fbc50b47e2f5afd
4
- data.tar.gz: a6c5a9db2ee07216abd703f9ca833e4f83110b19eeba0ad993327ad2ef963623
3
+ metadata.gz: fc01f58de2ca4d81c45420e08128c3ae0dd3f125badce5d62a8cc568c599d202
4
+ data.tar.gz: ad0724664a346aea50e4467d7a492737d885b6b2916f04077a73866b3ba0e915
5
5
  SHA512:
6
- metadata.gz: e47df55cfed559207ddf931b2c08b98cae32709534d2b612b6b94afd3bee3b79342a7c4dc558ca4eefc77a6e894c0e8d37bb09358275fa260c2ccaffac480235
7
- data.tar.gz: 077e24abc630b9588671e2eb8b3af7736f88299063e76b5176b84b681690c81eb89532057121a795ce2c50fcf34ad3c4356f298064186e156c778f2f9fdb7184
6
+ metadata.gz: b812adad30a746bc2b38f302047a595796d8ec2886b503a198b1384dd570d0b61d41de1029788c95b3f6a579c98c4571bef01c00c8f866aeb6245028676ab104
7
+ data.tar.gz: 11f506aa92cdc7ea0c5f219b84d694615c32da729ba57dfafa0f4eb9aa258d33c69c36383d858b0d0764c03362eb7e6db0b3fe4862b441db3d326e5c5f678f36
data/.irbrc CHANGED
@@ -1,3 +1,9 @@
1
- require_relative 'lib/conductor'
2
- include Conductor
1
+ require "irb/completion"
2
+ require_relative "lib/conductor"
3
3
 
4
+ include Conductor # standard:disable all
5
+
6
+ IRB.conf[:AUTO_INDENT] = true
7
+
8
+ require "awesome_print"
9
+ AwesomePrint.irb!
data/.rubocop.yml CHANGED
@@ -3,11 +3,11 @@ AllCops:
3
3
 
4
4
  Style/StringLiterals:
5
5
  Enabled: true
6
- EnforcedStyle: single_quotes
6
+ EnforcedStyle: double_quotes
7
7
 
8
8
  Style/StringLiteralsInInterpolation:
9
9
  Enabled: true
10
- EnforcedStyle: single_quotes
10
+ EnforcedStyle: double_quotes
11
11
 
12
12
  Layout/LineLength:
13
13
  Max: 120
data/CHANGELOG.md CHANGED
@@ -1,3 +1,37 @@
1
+ ### 1.0.8
2
+
3
+ 2024-04-27 14:01
4
+
5
+ #### NEW
6
+
7
+ - Add sequence: key to allow running a series of scripts/commands, each piping to the next
8
+ - Add `continue: true` for tracks to allow processing to continue after a script/command is successful
9
+ - `filename` key for comparing to just filename (instead of full
10
+ - Add `is a` tests for `number`, `integer`, and `float`
11
+ - Tracks in YAML config can have a title key that will be shown in STDERR 'Conditions met:' output
12
+ - Add `does not contain` handling for string and metadata comparisons
13
+
14
+ #### IMPROVED
15
+
16
+ - Allow `has yaml` or `has meta` (MultiMarkdown) as conditions
17
+
18
+ #### FIXED
19
+
20
+ - Use STDIN instead of reading file for conditionals
21
+ - String tests read STDIN input, not reading the file itself, allowing for piping between multiple scripts
22
+
23
+ ### 1.0.7
24
+
25
+ 2024-04-26 11:53
26
+
27
+ #### NEW
28
+
29
+ - Added test for MMD metadata, either for presence of meta or for specific keys or key values
30
+
31
+ #### FIXED
32
+
33
+ - Remove some debugging garbage
34
+
1
35
  ### 1.0.6
2
36
 
3
37
  2024-04-26 11:17
data/Gemfile.lock CHANGED
@@ -1,22 +1,32 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- marked-conductor (1.0.5)
4
+ marked-conductor (1.0.8)
5
5
  chronic (~> 0.10.2)
6
6
  tty-which (~> 0.5.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
+ ansi (1.5.0)
11
12
  ast (2.4.2)
12
13
  awesome_print (1.9.2)
13
14
  chronic (0.10.2)
14
15
  coderay (1.1.3)
16
+ diff-lcs (1.5.1)
17
+ docile (1.4.0)
18
+ gem-release (2.2.2)
15
19
  json (2.7.2)
16
20
  language_server-protocol (3.17.0.3)
17
21
  method_source (1.1.0)
22
+ multi_json (1.15.0)
18
23
  parallel (1.24.0)
19
- parser (3.3.0.5)
24
+ parse_gemspec (1.0.0)
25
+ parse_gemspec-cli (1.0.0)
26
+ multi_json
27
+ parse_gemspec
28
+ thor
29
+ parser (3.3.1.0)
20
30
  ast (~> 2.4.1)
21
31
  racc
22
32
  pry (0.14.2)
@@ -27,7 +37,20 @@ GEM
27
37
  rake (13.2.1)
28
38
  regexp_parser (2.9.0)
29
39
  rexml (3.2.6)
30
- rubocop (1.63.3)
40
+ rspec (3.13.0)
41
+ rspec-core (~> 3.13.0)
42
+ rspec-expectations (~> 3.13.0)
43
+ rspec-mocks (~> 3.13.0)
44
+ rspec-core (3.13.0)
45
+ rspec-support (~> 3.13.0)
46
+ rspec-expectations (3.13.0)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.13.0)
49
+ rspec-mocks (3.13.0)
50
+ diff-lcs (>= 1.2.0, < 2.0)
51
+ rspec-support (~> 3.13.0)
52
+ rspec-support (3.13.1)
53
+ rubocop (1.62.1)
31
54
  json (~> 2.3)
32
55
  language_server-protocol (>= 3.17.0)
33
56
  parallel (~> 1.10)
@@ -41,18 +64,39 @@ GEM
41
64
  rubocop-ast (1.31.2)
42
65
  parser (>= 3.3.0.4)
43
66
  ruby-progressbar (1.13.0)
67
+ simplecov (0.22.0)
68
+ docile (~> 1.1)
69
+ simplecov-html (~> 0.11)
70
+ simplecov_json_formatter (~> 0.1)
71
+ simplecov-console (0.9.1)
72
+ ansi
73
+ simplecov
74
+ terminal-table
75
+ simplecov-html (0.12.3)
76
+ simplecov_json_formatter (0.1.4)
77
+ terminal-table (3.0.2)
78
+ unicode-display_width (>= 1.1.1, < 3)
79
+ thor (1.3.1)
44
80
  tty-which (0.5.0)
45
81
  unicode-display_width (2.5.0)
82
+ yard (0.9.36)
46
83
 
47
84
  PLATFORMS
48
85
  arm64-darwin-20
49
86
 
50
87
  DEPENDENCIES
51
88
  awesome_print (~> 1.9.2)
89
+ bundler (~> 2.0)
90
+ gem-release (~> 2.2)
52
91
  marked-conductor!
92
+ parse_gemspec-cli (~> 1.0)
53
93
  pry (~> 0.14.2)
54
94
  rake (~> 13.0)
95
+ rspec (~> 3.0)
55
96
  rubocop (~> 1.21)
97
+ simplecov (~> 0.21)
98
+ simplecov-console (~> 0.9)
99
+ yard (~> 0.9, >= 0.9.26)
56
100
 
57
101
  BUNDLED WITH
58
102
  2.2.29
data/README.md CHANGED
@@ -64,6 +64,26 @@ tracks:
64
64
  command: obsidian-md-filter
65
65
  ```
66
66
 
67
+ #### Adding a title
68
+
69
+ Tracks can contain a `title` key. This is only used in the STDERR output of the track, where 'Met condition: ...' is shown for debugging. If a title is not present, the condition itself will be shown for debugging. If a title is defined, it replaces the condition in the STDERR output. This is mostly for shortening long condition strings to something more meaningful for debugging.
70
+
71
+ ### Sequencing
72
+
73
+ A track can also contain a sequence of scripts and/or commands. STDIN will be passed into the first script/command, then the STDOUT of that will be piped to the next script/command. To do this, add a key called `sequence` that contains an array of scripts and commands:
74
+
75
+ ```yaml
76
+ tracks:
77
+ - condition: phase is pro AND path contains README.md
78
+ sequence:
79
+ - script: strip_emoji
80
+ - command: rdiscount
81
+ ```
82
+
83
+ A sequence can not contain nested tracks.
84
+
85
+ By default, processing stops when a condition is met. If you want to continue processing after a condition is successful, add the `continue: true` to the track. This will only apply to tracks containing this key, and processing will stop when it gets to a successful condition that doesn't contain the `continue` key (or reaches the end of the tracks without another match).
86
+
67
87
  ### Conditions
68
88
 
69
89
  Available conditions are:
@@ -71,6 +91,7 @@ Available conditions are:
71
91
  - `extension` (or `ext`): This will test the extension of the file, e.g. `ext is md` or `ext contains task`
72
92
  - `tree contains ...`: This will test whether a given file or directory exists in any of the parent folders of the current file, starting with the current directory of the file. Example: `tree contains .obsidian` would test whether there was an `.obsidian` directory in any of the directories above the file (indicating it's within an Obsidian vault)
73
93
  - `path`: This tests just the path to the file itself, allowing conditions like `path contains _drafts` or `path does not contain _posts`.
94
+ - `filename`: Tests only the filename, can be any string comparison (`starts with`, `is`, `contains`, etc.).
74
95
  - `phase`: Tests whether Marked is in Preprocessor or Processor phase, allowing conditions like `phase is preprocess` or `phase is process` (which can be shortened to `pre` and `pro`).
75
96
  - `text`: This tests for any string match within the text of the document being processed. This can be used with operators `starts with`, `ends with`, or `contains`, e.g. `text contains @taskpaper` or `text does not contain <!--more-->`.
76
97
  - If the test value is surrounded by forward slashes, it will be treated as a regular expression. Regexes are always flagged as case insensitive. Use it like `text contains /@\w+/`.
@@ -82,6 +103,7 @@ Available conditions are:
82
103
  - If the YAML key is a date, it can be tested against with `before`, `after`, and `is`, and the value can be a natural language date, e.g. `yaml:date is after may 3, 2024`
83
104
  - If both the YAML key value and the test value are numbers, you can use operators `greater than` (`>`), `less than` (`<`), `equal`/`is` (`=`/`==`), and `is not equal`/`not equals` (`!=`/`!==`). Numbers will be interpreted as floats.
84
105
  - If the YAML value is a boolean, you can test with `is true` or `is not true` (or `is false`)
106
+ - `mmd` or `meta` will test for MultiMarkdown metadata using the same formatting as `yaml` above.
85
107
  - The following keywords act as a catchall and can be used as the last track in the config to act on any documents that aren't matched by preceding rules:
86
108
  - `any`
87
109
  - `else`
@@ -114,14 +136,24 @@ All of the [capabilities and requirements](https://marked2app.com/help/Custom_Pr
114
136
 
115
137
  A script run by Conductor already knows it has the right type of file with the expected data and path, so your script can focus on just processing one file type. It's recommended to separate all of that logic you may already have written out into separate scripts and let Conductor handle the forking based on various criteria.
116
138
 
139
+ > Custom processors **must** wait for input on STDIN. Most markdown CLIs will do this automatically, but scripts should include a call to read STDIN. This will pause the script and wait for the data to be sent. Without this, Marked will launch the script, and if it closes the pipe, it will try to write data to a closed pipe and crash immediately. This is a very difficult error to trap in Marked, so it's crucial that all scripts keep the STDIN pipe open.
140
+
141
+
117
142
  ## Tips
118
143
 
144
+ - Config file must be valid YAML. Any value containing colons, brackets, or other special characters should be quoted, e.g. (`condition: "text contains my:text"`)
119
145
  - You can see what condition matched in Marked by opening <b>Help->Show Custom Processor Log</b> and checking the STDERR output.
120
- - To run [a custom processor for Bear](https://brettterpstra.com/2023/10/08/marked-and-bear/), use the condition `text contains /source: *bear/`
146
+ - To run [a custom processor for Bear](https://brettterpstra.com/2023/10/08/marked-and-bear/), use the condition `"text contains <!-- source: bear.app -->"`
121
147
  - To run a custom processor for Obsidian, use the condition `tree contains .obsidian`
122
148
 
123
149
  ## Testing
124
150
 
151
+ You can test conductor setups using Marked's `Help->Show Custom Processor Log`, or by running from the command line. The easiest way to test conditions is to set the track's command to `echo "meaningful definition"` and see what conditions are met when conductor is run.
152
+
153
+ In Marked's Custom Processor Log, you can see both the STDOUT output and the STDERR messages. When running Conductor, the STDERR output will show what conditions were met (as well as any errors reported).
154
+
155
+ ### From the command line
156
+
125
157
  In order to test from the command line, you'll need certain environment variables set. This can be done by exporting the following variables with your own definitions, or by running conductor with all of the variables preceding the command, e.g. `$ MARKED_ORIGIN=/path/to/markdown_file.md [...] conductor`.
126
158
 
127
159
  The following need to be defined. Some can be left as empty or to defaults, such as `MARKED_INCLUDES` and `MARKED_OUTLINE`, but all need to be set to something.
data/README.rdoc ADDED
@@ -0,0 +1,6 @@
1
+ = Marked Commander
2
+
3
+ A command line tool that functions as a custom processor handler for Marked 2.
4
+
5
+
6
+
data/Rakefile CHANGED
@@ -2,6 +2,43 @@
2
2
 
3
3
  require "bundler/gem_tasks"
4
4
  require "rubocop/rake_task"
5
+ require "rspec/core/rake_task"
6
+ require "rdoc/task"
7
+ require "yard"
8
+
9
+ Rake::RDocTask.new do |rd|
10
+ rd.main = "README.rdoc"
11
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb", "bin/**/*")
12
+ rd.title = "Marked Conductor"
13
+ end
14
+
15
+ YARD::Rake::YardocTask.new do |t|
16
+ t.files = ["lib/conductor/*.rb"]
17
+ t.options = ["--markup-provider=redcarpet", "--markup=markdown", "--no-private", "-p", "yard_templates"]
18
+ # t.stats_options = ['--list-undoc']
19
+ end
20
+
21
+ RSpec::Core::RakeTask.new(:spec) do |t|
22
+ t.rspec_opts = "--pattern spec/*_spec.rb"
23
+ end
24
+
25
+ task default: %i[test]
26
+
27
+ desc "Alias for build"
28
+ task package: :build
29
+
30
+ task test: "spec"
31
+ task lint: "standard"
32
+ task format: "standard:fix"
33
+
34
+ desc "Open an interactive ruby console"
35
+ task :console do
36
+ require "irb"
37
+ require "bundler/setup"
38
+ require "conductor"
39
+ ARGV.clear
40
+ IRB.start
41
+ end
5
42
 
6
43
  RuboCop::RakeTask.new
7
44
 
@@ -13,7 +50,7 @@ task package: :build
13
50
  desc "Development version check"
14
51
  task :ver do
15
52
  gver = `git ver`
16
- cver = IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
53
+ cver = IO.read(File.join(File.dirname(__FILE__), "CHANGELOG.md")).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
17
54
  res = `grep VERSION lib/conductor/version.rb`
18
55
  version = res.match(/VERSION *= *['"](\d+\.\d+\.\d+(\w+)?)/)[1]
19
56
  puts "git tag: #{gver}"
data/bin/conductor CHANGED
@@ -12,38 +12,67 @@ config = Config.new
12
12
 
13
13
  stdin = Conductor.stdin
14
14
 
15
+ def execute_track(track)
16
+ if track[:sequence]
17
+ track[:sequence].each do |cmd|
18
+ if cmd[:script]
19
+ script = Script.new(cmd[:script])
20
+
21
+ res = script.run
22
+ elsif cmd[:command]
23
+ command = Command.new(cmd[:command])
24
+
25
+ res = command.run
26
+ end
27
+
28
+ Conductor.stdin = res unless res.nil?
29
+ end
30
+ elsif track[:script]
31
+ script = Script.new(track[:script])
32
+
33
+ Conductor.stdin = script.run
34
+ elsif track[:command]
35
+ command = Command.new(track[:command])
36
+
37
+ Conductor.stdin = command.run
38
+ end
39
+
40
+ Conductor.stdin
41
+ end
42
+
15
43
  def conduct(tracks, res = nil, condition = nil)
16
44
  tracks.each do |track|
17
45
  cond = Condition.new(track[:condition])
18
46
 
19
47
  next unless cond.true?
20
48
 
21
- if track[:tracks]
22
- ts = track[:tracks].symbolize_keys
23
-
24
- res, condition = conduct(ts, res, condition)
25
49
 
26
- next if res.nil?
27
- end
50
+ # Build "matched condition" message
51
+ title = track[:title] || track[:condition]
52
+ condition ||= ['']
53
+ condition << title
54
+ condition.push(track.key?(:continue) ? ', ' : ' -> ')
28
55
 
29
- condition = condition.nil? ? track[:condition] : "#{track[:condition]} > #{condition}"
56
+ res = execute_track(track)
30
57
 
31
- if track[:script]
32
- script = Script.new(track[:script])
58
+ if track[:tracks]
59
+ ts = track[:tracks]
33
60
 
34
- res = script.run
35
- elsif track[:command]
36
- command = Command.new(track[:command])
61
+ res, condition = conduct(ts, res, condition)
37
62
 
38
- res = command.run
63
+ next if res.nil?
39
64
  end
40
65
 
41
- break
66
+ break unless track[:continue]
42
67
  end
43
68
 
44
69
  [res, condition]
45
70
  end
46
71
 
72
+ def clean_condition(condition)
73
+ condition.join('').sub(/ *(->|,) *$/, '')
74
+ end
75
+
47
76
  options = {}
48
77
  optparse = OptionParser.new do|opts|
49
78
  opts.banner = "Called from Marked 2 as a Custom Pre/Processor"
@@ -68,7 +97,7 @@ if res.nil?
68
97
  $stderr.puts('No conditions satisfied')
69
98
  puts 'NOCUSTOM'
70
99
  else
71
- $stderr.puts("Met condition: #{condition}")
100
+ $stderr.puts("Met condition: #{clean_condition(condition)}")
72
101
  puts res
73
102
  end
74
103
 
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ::Array
4
-
5
4
  def symbolize_keys!
6
5
  replace symbolize_keys
7
6
  end
@@ -8,44 +8,43 @@ module Conductor
8
8
  def initialize(command)
9
9
  parts = Shellwords.split(command)
10
10
  self.path = parts[0]
11
- self.args = parts[1..].join(' ')
11
+ self.args = parts[1..].join(" ")
12
12
  end
13
13
 
14
14
  def path=(path)
15
- @path = if path =~ %r{^[%/]}
16
- File.expand_path(path)
17
- else
18
- which = TTY::Which.which(path)
19
- which || path
20
- end
15
+ @path = if %r{^[%/]}.match?(path)
16
+ File.expand_path(path)
17
+ else
18
+ which = TTY::Which.which(path)
19
+ which || path
20
+ end
21
21
  end
22
22
 
23
23
  def args=(array)
24
24
  @args = if array.is_a?(Array)
25
- array.join(' ')
26
- else
27
- array
28
- end
25
+ array.join(" ")
26
+ else
27
+ array
28
+ end
29
29
  end
30
30
 
31
31
  def run
32
32
  stdin = Conductor.stdin
33
33
 
34
- raise 'Command path not found' unless @path
34
+ raise "Command path not found" unless @path
35
35
 
36
36
  use_stdin = true
37
- if args =~ /\$\{?file\}?/
37
+ if /\$\{?file\}?/.match?(args)
38
38
  use_stdin = false
39
39
  args.sub!(/\$\{?file\}?/, %("#{Env.env[:filepath]}"))
40
40
  else
41
- raise 'No input' unless stdin
41
+ raise "No input" unless stdin
42
42
 
43
43
  end
44
44
 
45
45
  if use_stdin
46
46
  `echo #{Shellwords.escape(stdin)} | #{Env} #{path} #{args}`
47
47
  else
48
- puts "#{Env} #{path} #{args}"
49
48
  `#{Env} #{path} #{args}`
50
49
  end
51
50
  end