README.md in whirled_peas-0.4.1 vs README.md in whirled_peas-0.5.0

- old
+ new

@@ -34,12 +34,12 @@ # visualize.rb require 'whirled_peas' class TemplateFactory def build(frame, args) - WhirledPeas.template do |body| - body.add_box('Title') do |_, settings| + WhirledPeas.template do |composer| + composer.add_box('Title') do |_, settings| settings.underline = true "Hello #{args[:name]}" end # ... end @@ -68,14 +68,14 @@ The optional loading screen can be configured like ```ruby class LoadingTemplateFactory def build - WhirledPeas.template do |t| - t.add_box('Loading') do |box, settings| + WhirledPeas.template do |composer| + composer.add_box('Loading') do |_, settings| settings.set_margin(top: 15) - settings.auto_margin = true + settings.align = :center settings.full_border(color: :blue, style: :double) "Loading..." end end end @@ -111,11 +111,11 @@ def send_frame(name, duration:, args:) # implementation end ``` -**IMPORTANT**: the keys for arguments must be symbols. +**IMPORTANT**: the keys in the `args` hash must be symbols! #### Example Simple application that loads a set of numbers and looks for a pair that adds up to 1,000 @@ -146,118 +146,130 @@ end ``` ### Template Factory -To render the frame events sent by the driver, the application requires a template factory. This factory will be called for each frame event, with the frame name and the arguments supplied by the driver. A template factory can be a simple ruby class and thus can maintain state. Whirled Peas provides a few basic building blocks to make simple, yet elegant terminal-based UIs. +To render the frame events sent by the driver, the application requires a template factory. This factory will be called for each frame event, with the frame name and the arguments supplied by the driver. A template factory can be an instance of ruby class and thus can maintain state. Whirled Peas provides a few basic building blocks to make simple, yet elegant terminal-based UIs. #### Loading Template Factory -`WhirledPeas.start` takes an optional template facotry to build a loading screen. This instance must implement `#build` (taking no arguments). The template returned by that method will be painted while the event loop is waiting for frames. The factory method will be called once per refresh cycle, so it's possible to implement animation. +`WhirledPeas.configure` takes an optional template factory to build a loading screen. This instance must implement `#build` (taking no arguments). The template returned by that method will be painted while the event loop is waiting for frames. The factory method will be called once per refresh cycle, so it's possible to implement animation. #### Building Blocks -A template is created with `WhirledPeas.template`, which yields a `Template` object and `TemplateSettings`. This template object is a `ComposableElement`, which allows for attaching child elements and setting layout options. `GridElement` and `BoxElement` are two other composable elements and `TextElement` is a simple element that can hold a text/number value and has layout options, but cannot have any child elements. +A template is created with `WhirledPeas.template`, which yields a `Composer` object for a `BoxElement` and `BoxSettings`. The composer allows for attaching child elements and the settings for setting layout options. -A `ComposableElement` provides the following methods to add child elements, each of these takes an optional string argument that is set as the name of the element (which can be useful when debugging). +A `Composer` provides the following methods to add child elements, each of these takes an optional string argument that is set as the name of the element (which can be useful when debugging). -- `add_box` - yields a `ComposableElement` and a `BoxSettings`, which will be added to the parent's children -- `add_grid` - yields a `ComposableElement` and a `GridSettings`, which will be added to the parent's children +- `add_box` - yields a `Composer` and a `BoxSettings`, which will be added to the parent's children +- `add_grid` - yields a `Composer` and a `GridSettings`, which will be added to the parent's children - `add_text` - yields `nil` and a `TextSettings`, which will be added to the parent's children E.g. ```ruby -WhirledPeas.template do |template, template_settings| - template_settings.bg_color = :blue - template.add_grid do |grid, grid_settings| - grid_settings.num_cols = 10 +WhirledPeas.template do |composer, settings| + settings.bg_color = :blue + composer.add_grid do |composer, settings| + settings.num_cols = 10 100.times do |i| - grid.add_text { i } + composer.add_text { i } end end end ``` The above template can also be broken down into more manageable methods, e.g. ```ruby -def number_grid(grid, settings) +def number_grid(_composer, settings) settings.num_cols = 10 - 100.times do |i| - grid.add_text { i } - end + 100.times.map(&:itself) end -WhirledPeas.template do |template, settings| +WhirledPeas.template do |composer, settings| settings.bg_color = :blue - template.add_grid(&method(:number_grid)) + composer.add_grid(&method(:number_grid)) end ``` Additionally, if no child element is explicitly added to a `GridElement`, but the block returns an array of strings or numbers, those will be converted to `TextElements` and added as children to the `GridElement`. For example, these are identical ways to create a grid of strings ```ruby -template.add_grid do |g| +template.add_grid do |composer| 100.times do |i| - g.add_text { i } + composer.add_text { i } end end -template.add_grid do |g| +template.add_grid do 100.times.map(&:itself) end ``` Similarly, if no child element is explicilty added to a `BoxElement`, but the block returns a string or number, that value will be converted to a `TextElement` and added as a child. For example, these are identical ways to create a box with string content ```ruby -template.add_box do |b| - b.add_text { "Hello!" } +template.add_box do |composer| + composer.add_text { "Hello!" } end -template.add_box do |b| +template.add_box do "Hello!" end ``` #### Settings -Each element type has an associated settings type, which provide a custom set of options to format the output. Parent settings may be merged into child settings (assuming the child supports those settings) +Each element type has an associated settings type, which provide a custom set of options to format the output. Child settings will inherit from the parent, where applicable The available settigs are -| Setting | Description | Default | Availability | Merged? | -| ------------- | ------------------------------------------------------------------ | ------- | --------------------------------- | ------- | -| `align` | Justifies the text (allowed values: `:left`, `:center`, `:right`) | `:left` | `Box`, `Grid`, `Text` | Yes | -| `auto_margin` | Evenly distribute side margin (overrides left/right in `margin`) | `false` | `Box`, `Grid` | Yes | -| `bg_color` | Background color (see [Colors](#colors)) | | `Box`, `Grid`, `Template`, `Text` | Yes | -| `bold` | `true` makes the font bold | `false` | `Box`, `Grid`, `Template`, `Text` | Yes | -| `border` | Set the border for the lements | none | `Box`, `Grid`, | Yes | -| `color` | Foreground text color (see [Colors](#colors)) | | `Box`, `Grid`, `Template`, `Text` | Yes | -| `flow` | Flow to display child elements (see [Display Flow](#display-flow)) | `:l2r` | `Box` | Yes | -| `margin` | Set the (left, top, right, bottom) margin of the element | `0` | `Box`, `Grid` | Yes | -| `padding` | Set the (left, top, right, bottom) padding of the element | `0` | `Box`, `Grid` | Yes | -| `title_font` | Font used to create "large" text (see [Large Text](#large-text)) | | `Text` | -| `transpose` | Display grid elements top-to-bottom, then left-to-right | `false` | `Grid` | No | -| `underline` | `true` underlines the font | `false` | `Box`, `Grid`, `Template`, `Text` | Yes | -| `width` | Override the calculated with of an element | | `Box`, `Grid`, `Text` | No | +| Setting | Description | Default | Availability | Inherited | +| ------------ | ------------------------------------------------------------------------------- | ------- | --------------------- | -------------------- | +| `align` | Justifies the content (allowed values: `:left`, `:center`, `:right`) | `:left` | `Box`, `Grid` | Yes | +| `bg_color` | Background color (see [Colors](#colors)) | | `Box`, `Grid`, `Text` | Yes | +| `bold` | `true` makes the font bold | `false` | `Box`, `Grid`, `Text` | Yes | +| `border` | Set the border for the lements | none | `Box`, `Grid`, | Only style and color | +| `color` | Foreground text color (see [Colors](#colors)) | | `Box`, `Grid`, `Text` | Yes | +| `flow` | Flow to display child elements (see [Display Flow](#display-flow)) | `:l2r` | `Box`, `Grid` | Yes | +| `height` | Override the calculated height of an element's content area | | `Box`, `Grid` | No | +| `margin` | Set the (left, top, right, bottom) margin of the element | `0` | `Box`, `Grid` | No | +| `num_cols` | Number of columns in the grid (must be set!) | | `Grid` | No | +| `padding` | Set the (left, top, right, bottom) padding of the element | `0` | `Box`, `Grid` | No | +| `position` | Set the (left, top) position of the element relative to parent content area | `0` | `Box`, `Grid` | No | +| `title_font` | Font used for "large" text (see [Large Text](#large-text), ignores `underline`) | | `Text` | No | +| `underline` | `true` underlines the font | `false` | `Box`, `Grid`, `Text` | Yes | +| `width` | Override the calculated width of an element's content area | | `Box`, `Grid` | No | -##### Margin and Padding +##### Position -Margin and padding settings allow for setting the spacing on each of the 4 sides of the element independently. The set these values, use +Position settings dictate the relative position from where the painter would have preferred to place the container. Negative numbers move the container left/up and positive numbers move it right/down. To set these values, use -- `clear_margin` - sets all margin values to 0 +- `set_position(left:, top:)` + +##### Margin + +Margin settings dictate the spacing on the outside (i.e. outside of the border) of each of the 4 sides of the container independently. To set these values, use + - `set_margin(left:, top:, right:, bottom:)` -- `clear_padding` - sets all margin values to 0 + +Note: values cannot be negative + +##### Padding + +Padding settings dictate the spacing on the inside (i.e. inside of the border) of each of the 4 sides of the container independently. To set these values, use + - `set_padding(left:, top:, right:, bottom:)` +Note: values cannot be negative + ##### Border The border settings consist of 6 boolean values (border are either width 1 or not shown), the 4 obvious values (`left`, `top`, `right`, and `bottom`) along with 2 other values for inner borders (`inner_horiz` and `inner_vert`) in a grid. A border also has a foreground color (defaults to `:white`) and a style. The background color is determined by the `bg_color` of the element. Border values can be set with -- `clear_border` - sets all border positions to `false` - `set_border(left:, top:, right:, bottom:, inner_horiz:, inner_vert:, color:, style:)` +- `full_border(style:, color:)` Available border styles are - `:bold` (default) @@ -371,12 +383,17 @@ ```ruby class TemplateFactory def build(frame, args) set_state(frame, args) - WhirledPeas.template do |t| - t.add_box('Body', &method(:body)) + WhirledPeas.template do |composer, settings| + settings.flow = :l2r + settings.align = :center + + composer.add_box('Title', &method(:title)) + composer.add_box('Sum', &method(:sum)) + composer.add_grid('NumberGrid', &method(:number_grid)) end end private @@ -386,38 +403,29 @@ @sum = args[:sum] if args.key?(:sum) @low = args[:low] if args.key?(:low) @high = args[:high] if args.key?(:high) end - def title(_elem, settings) + def title(_composer, settings) settings.underline = true "Pair Finder" end - def sum(_elem, settings) + def sum(_composer, settings) settings.color = @frame == 'found-pair' ? :green : :red @sum ? "Sum: #{@sum}" : 'N/A' end - def number_grid(elem, settings) + def number_grid(composer, settings) settings.full_border @numbers.each.with_index do |num, index| - g.add_text do |_, settings| + composer.add_text do |_, settings| settings.bg_color = (@low == index || @high == index) ? :cyan : :white num end end end - - def body(elem, settings) - settings.flow = :l2r - settings.auto_margin = true - - elem.add_box('Title', &method(:title)) - elem.add_box('Sum', &method(:sum)) - elem.add_grid('NumberGrid', &method(:number_grid)) - end end ``` ### Debugging @@ -441,23 +449,38 @@ ``` $ whirled_peas <config file> play_frame move '{"direction":"N"}' ``` -Adding the `--debug` flag will result in just printing out the template's debug information, e.g. +Adding the `--template` flag will result in printing out debug information for the template, e.g. ``` -$ whirled_peas <config file> play_frame move '{"direction":"N"}' --debug -+ TEMPLATE [WhirledPeas::UI::Template] +$ whirled_peas <config file> play_frame move '{"direction":"N"}' --template ++ TEMPLATE [WhirledPeas::Template::BoxElement] - Settings - WhirledPeas::UI::TemplateSettings + WhirledPeas::Settings::BoxSettings <default> - Children - + TitleContainer [WhirledPeas::UI::BoxElement] + + TitleContainer [WhirledPeas::Template::BoxElement] ... ``` +Adding the `--painter` flag will result in printing out debug information the painter (the rendered template), e.g. + +``` +$ whirled_peas <config file> play_frame move '{"direction":"N"}' --painter ++ TEMPLATE [WhirledPeas::Graphics::BoxPainter] + - Settings + WhirledPeas::Settings::BoxSettings + <default> + - Dimensions + - Canvas: + - Children + + TitleContainer [WhirledPeas::Graphics::BoxPainter] +... +``` + #### loading Displays the configured loading screen for several seconds ``` @@ -480,9 +503,24 @@ ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). + +### Testing + +In addition to standard RSpec tests, WhirledPeas has custom tests for rendered templates. These files live in `screen_test/rendered`. Each ruby file is expected to define a class named `TemplateFactory` that responds to `#build(name, args)` returning a template (the standard template factory role). Each file should also be accompanied by a `.frame` file with the same base name. This file will contain the output of the rendered screen and is considered the correct output when running tests. + +Note: viewing `.frame` files with `cat` works better than most other text editors. + +The following rake tasks are provided to interact with the screen tests + +- `screen_test` runs all screen tests in the `screen_test/rendered` directory +- `screen_test:debug[path/to/file.rb]` print the rendered template debug tree +- `screen_test:run[path/to/file.rb]` runs a single screen test +- `screen_test:save[path/to/file.rb]` saves the output generated by the template in the `.frame` file, overwriting any existing file +- `screen_test:view[path/to/file.rb]` views the output generated by the template +- `screen_test:update_all[path/to/file.rb]` interactively step through each pending or failed screen test to compare/set the expected output ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/whirled_peas. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/whirled_peas/blob/master/CODE_OF_CONDUCT.md).