Skip to content

Commit

Permalink
Introduce print.text and print.erb
Browse files Browse the repository at this point in the history
See the README for more details.
  • Loading branch information
schneems committed May 6, 2024
1 parent d479e8d commit 74c6cb9
Show file tree
Hide file tree
Showing 9 changed files with 486 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## HEAD

- Add: `print.text` and `print.erb` see README for usage details
- Add: Output documents include more frequent warnings that the document was autogenerated and should not be modified
- Breaking: Remove repl_runner support.
- Fix `file.write` do not prepend "In file x ..." when the command is hidden i.e. `:::-> file.write`
Expand Down
99 changes: 96 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ This will generate a project folder with your project in it, and a markdown READ
- Execute Bash Commands
- [$](#shell-commands)
- [fail.$](#shell-commands)
- Printing
- [print.text](#print)
- [print.erb](#print)
- Chain commands
- [pipe](#pipe)
- [|](#pipe)
Expand Down Expand Up @@ -181,7 +184,7 @@ RunDOC only cares about things that come after a `:::` section. If you have a "r
You can mix non-command code and commands, as long as the things that aren't rendering come first. This can be used to "fake" a command, for example:

```
$ rails new myapp # Not a command since it's missing the ":::>>""
$ rails new myapp # Not a command since it's missing the ":::>>"
:::-> $ rails new myapp --skip-test --skip-yarn --skip-sprockets
:::>> | $ head -n 5
```
Expand All @@ -197,13 +200,12 @@ $ rails new myapp # Not a command since it's missing the ":::>>""
create config.ru
```

It looks like the command was run without any flags, but in reality `rails new myapp --skip-test --skip-yarn --skip-sprockets | head -n 5` was executed.
In this example it looks like the command was run without any flags, but in reality `rails new myapp --skip-test --skip-yarn --skip-sprockets | head -n 5` was executed. Though it's more explicit to use a `print.text` block, see [#print.text](#print) for more info.

## Rendering Cheat Sheet

An arrow `>` is shorthand for "render this" and a dash `-` is shorthand for skip this section. The two positions are **command** first and **result** second.


- `:::>-` (YES command output, not result output)
- `:::>>` (YES command output, YES result output)
- `:::--` (not command output, not result output)
Expand Down Expand Up @@ -250,6 +252,64 @@ These custom commands are kept to a minimum, and for the most part behave as you

Running shell commands like this can be very powerful, you'll likely want more control of how you manipulate files in your project. To do this you can use the `file.` namespace:

## Print

Current commands:

- `print.text`
- `print.erb`

Behaves slightly differently than other commands. The "command" portion of the control character i.e. `:::>` controls whether the contents will be rendered inside the block or before the block (versus usually this is used to control if the command such as `$ cd` is shown).

- `:::>>` Print inside the code block
- `:::->` Print BEFORE the code block, if multiple calls are made, they will be displayed in order.
- `:::--` Nothing will be rendered, can be used to pass data to another rundoc command via the pipe operator.
- `:::>-` Same behavior as `:::--`.

This functionality is present to allow body text to be generated (versus only allowing generated text in code blocks).

Use the `print.text` keyword followed by what you want to print:

```
:::-> print.text
I will render BEFORE the code block, use :::>> to render in it.

It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness,
it was the epoch of belief, it was the epoch ...
```

Specifying `:::->` with `print.text` will render text without a code block (or before the code block if there are other rundoc commands). If you want to render text with a code block you can do it via `:::>>`.

To dynamically change the contents of the thing you're printing you can use `print.erb`:

```
:::-> print.erb
I will render BEFORE the code block, use :::>> to render in it.

What a week!
Captain it's only <%= Time.now.strftime("%A") %>!
```

This will evaluate the context of ERB and write it to the file. Like `print.text` use `:::->` to write the contents without a code block (or before the code block if there are other rundoc commands). If you want to render text with a code block you can do it via `:::>>`.

ERB commands share a default context. That means you can set a value in one `print.erb` section and view it from another. If you want to isolate your erb blocks you can provide a custom name via the `binding:` keyword:

```
:::>> print.erb(binding: "mc_hammer")
I will render IN a code block, use `:::->` to render before.

<%= @stop = true %>

:::>> print.erb(binding: "different")
<% if @stop %>
Hammer time
<% else %>
Can't touch this
<% end %>
```

In this example setting `@stop` in one `print.erb` will have no effect on the other.

## File Commands

Current Commands:
Expand Down Expand Up @@ -504,6 +564,39 @@ Sometimes sensitive info like usernames, email addresses, or passwords may be in

This command `filter_sensitive` can be called multiple times with different values. Since the config is in Ruby you could iterate over an array of sensitive data

## Writing a new command

Rundoc does not have a stable internal command interface. You can define your own commands, but unless it is committed in this repo, it may break on a minor version change.

To add a new command it needs to be parsed and called. Examples of commands being implemented are seen in `lib/rundoc/code_command`.

A new command needs to be registered:

```
Rundoc.register_code_command(:lol, Rundoc::CodeCommand::Lol)
```

They should inherit from Rundoc::CodeCommand:

```
class Rundoc::CodeCommand::Lol < Rundoc::CodeCommand
def initialize(line)
end
end
```

The initialize method is called with input from the document. The command is rendered (`:::>-`) by the output of the `def call` method. The contents produced by the command (`:::->`) are rendered by the `def to_md` method.

The syntax for commands is ruby-ish but it is a custom grammar implemented in `lib/peg_parser.rb` for more info on manipulating the grammar see this tutorial on how I added keword-like/hash-like syntax https://github.com/schneems/implement_ruby_hash_syntax_with_parslet_example.

Command initialize methods natively support:

- Barewords as a single string input
- Keyword arguments
- A combination of the two

Anything that is passed to the command via "stdin" is available via a method `self.contents`. The interplay between the input and `self.contents` is not strongly defined.

## Copyright

All content Copyright Richard Schneeman © 2020
2 changes: 2 additions & 0 deletions lib/rundoc/code_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,5 @@ def to_md(env = {})
require "rundoc/code_command/raw"
require "rundoc/code_command/background"
require "rundoc/code_command/website"
require "rundoc/code_command/print/text"
require "rundoc/code_command/print/erb"
50 changes: 50 additions & 0 deletions lib/rundoc/code_command/print/erb.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

require "erb"

class EmptyBinding
def self.create
self.new.empty_binding
end

def empty_binding
binding
end
end

RUNDOC_ERB_BINDINGS = Hash.new {|h, k| h[k] = EmptyBinding.create }

class Rundoc::CodeCommand
class PrintERB < Rundoc::CodeCommand

def initialize(line = nil, binding: "default")
@line = line
@binding = RUNDOC_ERB_BINDINGS[binding]
end

def to_md(env)
if render_before?
env[:before] << render
end

""
end

def render
@render ||= ERB.new([@line, contents].compact.join("\n")).result(@binding)
end

def call(erb={})
if render_before?
""
else
render
end
end

def render_before?
!render_command? && render_result?
end
end
end

Rundoc.register_code_command(:"print.erb", Rundoc::CodeCommand::PrintERB)
33 changes: 33 additions & 0 deletions lib/rundoc/code_command/print/text.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class Rundoc::CodeCommand
class PrintText < Rundoc::CodeCommand
def initialize(line)
@line = line
end

def to_md(env)
if render_before?
env[:before] << [@line, contents].compact.join("\n")
end

""
end

def hidden?
!render_result?
end

def call(env = {})
if render_before?
""
else
[@line, contents].compact.join("\n")
end
end

def render_before?
!render_command? && render_result?
end
end
end

Rundoc.register_code_command(:"print.text", Rundoc::CodeCommand::PrintText)
16 changes: 15 additions & 1 deletion lib/rundoc/code_section.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,21 @@ def render

return "" if hidden?

array = [env[:before], env[:fence_start], result, env[:fence_end], env[:after]]
array = [env[:before]]

result.flatten!
result.compact!
result.map! {|s| s.respond_to?(:rstrip) ? s.rstrip : s }
result.reject!(&:empty?)
result.map!(&:to_s)

if !result.empty?
array << env[:fence_start]
array << result
array << env[:fence_end]
end
array << env[:after]

array.flatten!
array.compact!
array.map! {|s| s.respond_to?(:rstrip) ? s.rstrip : s }
Expand Down
Loading

0 comments on commit 74c6cb9

Please sign in to comment.