{"id":21202483,"url":"https://github.com/infrablocks/lino","last_synced_at":"2025-04-03T02:10:37.391Z","repository":{"id":38356335,"uuid":"80256234","full_name":"infrablocks/lino","owner":"infrablocks","description":"Command line builder and execution utilities.","archived":false,"fork":false,"pushed_at":"2025-03-26T14:42:38.000Z","size":446,"stargazers_count":15,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-26T20:11:11.179Z","etag":null,"topics":["builder","command","command-line","commandline","gem","infrablocks","ruby","ruby-gem","ruby-library","rubygem"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/infrablocks.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-01-27T23:58:46.000Z","updated_at":"2025-03-26T14:42:41.000Z","dependencies_parsed_at":"2023-02-11T03:15:49.606Z","dependency_job_id":"501ceda7-28f3-4045-8455-6d4fefe040a0","html_url":"https://github.com/infrablocks/lino","commit_stats":{"total_commits":196,"total_committers":5,"mean_commits":39.2,"dds":0.5561224489795918,"last_synced_commit":"7cf6eef65c0001ba9c06af2a8e9d5c32614dc5c0"},"previous_names":[],"tags_count":70,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrablocks%2Flino","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrablocks%2Flino/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrablocks%2Flino/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrablocks%2Flino/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/infrablocks","download_url":"https://codeload.github.com/infrablocks/lino/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246922247,"owners_count":20855345,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["builder","command","command-line","commandline","gem","infrablocks","ruby","ruby-gem","ruby-library","rubygem"],"created_at":"2024-11-20T20:16:07.398Z","updated_at":"2025-04-03T02:10:37.374Z","avatar_url":"https://github.com/infrablocks.png","language":"Ruby","readme":"# Lino\n\nCommand line building and execution utilities.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'lino'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install lino\n\n## Usage\n\nLino allows commands to be built and executed:\n\n```ruby\nrequire 'lino'\n  \ncommand_line = Lino.builder_for_command('ruby')\n    .with_flag('-v')\n    .with_option('-e', 'puts \"Hello\"')\n    .build\n    \nputs command_line.array\n# =\u003e ['ruby', '-v', '-e', 'puts \"Hello\"']\n  \nputs command_line.string\n# =\u003e ruby -v -e puts \"Hello\"\n  \ncommand_line.execute \n# ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]\n# Hello\n```\n\n### Building command lines\n\n`Lino` supports building command lines via instances of the \n`Lino::Builder::CommandLine` class. `Lino::Builder::CommandLine` allows a \nnumber of different styles of commands to be built. The object built by \n`Lino::Builder::CommandLine` is an instance of `Lino::Model::CommandLine`, which\nrepresents the components and context of a command line and allows the \ncommand line to be executed. \n\nAside from the object model, `Lino::Model::CommandLine` instances have two \nrepresentations, accessible via the `#string` and `#array` instance methods.\n\nThe string representation is useful when the command line is intended to be\nexecuted by a shell, where quoting is important. However, it can present a \nsecurity risk if the components (option values, arguments, environment \nvariables) of the command line are user provided. For this reason, the array\nrepresentation is preferable and is the representation used by default whenever \n`Lino` executes commands.\n\n#### Getting a command line builder\n\nA `Lino::Builder::CommandLine` can be instantiated using:\n\n```ruby\nLino.builder_for_command('ls')\n```\n\nor using the now deprecated:\n\n```ruby\nLino::CommandLineBuilder.for_command('ls')\n```\n\n#### Flags\n\nFlags can be added with `#with_flag`:\n\n```ruby\ncommand_line = Lino.builder_for_command('ls')\n    .with_flag('-l')\n    .with_flag('-a')\n    .build\n\ncommand_line.array\n# =\u003e [\"ls\", \"-l\", \"-a\"]\ncommand_line.string\n# =\u003e \"ls -l -a\"\n```\n\nor `#with_flags`:\n\n```ruby\ncommand_line = Lino.builder_for_command('ls')\n    .with_flags(%w[-l -a])\n    .build\n\ncommand_line.array\n# =\u003e [\"ls\", \"-l\", \"-a\"]\ncommand_line.string\n# =\u003e \"ls -l -a\"\n```\n\n#### Options\n\nOptions with values can be added with `#with_option`:\n\n```ruby\ncommand_line = Lino.builder_for_command('gpg')\n    .with_option('--recipient', 'tobyclemson@gmail.com')\n    .with_option('--sign', './doc.txt')\n    .build\n\ncommand_line.array\n# =\u003e [\"gpg\", \"--recipient\", \"tobyclemson@gmail.com\", \"--sign\", \"./doc.txt\"]\ncommand_line.string\n# =\u003e \"gpg --recipient tobyclemson@gmail.com --sign ./doc.txt\"\n\n```\n\nor `#with_options`, either as a hash:\n\n```ruby\ncommand_line = Lino.builder_for_command('gpg')\n    .with_options({\n      '--recipient' =\u003e 'tobyclemson@gmail.com',\n      '--sign' =\u003e './doc.txt'\n    })\n    .build\n\ncommand_line.array\n# =\u003e [\"gpg\", \"--recipient\", \"tobyclemson@gmail.com\", \"--sign\", \"./doc.txt\"]\ncommand_line.string\n# =\u003e \"gpg --recipient tobyclemson@gmail.com --sign ./doc.txt\"\n```\n\nor as an array:\n\n```ruby\ncommand_line = Lino.builder_for_command('gpg')\n    .with_options(\n      [\n        { option: '--recipient', value: 'tobyclemson@gmail.com' },\n        { option: '--sign', value: './doc.txt' }\n      ]\n    )\n    .build\n\ncommand_line.array\n# =\u003e [\"gpg\", \"--recipient\", \"tobyclemson@gmail.com\", \"--sign\", \"./doc.txt\"]\ncommand_line.string\n# =\u003e \"gpg --recipient tobyclemson@gmail.com --sign ./doc.txt\"\n```\n\nSome commands allow options to be repeated:\n\n```ruby\ncommand_line = Lino.builder_for_command('example.sh')\n    .with_repeated_option('--opt', ['file1.txt', nil, '', 'file2.txt'])\n    .build\n\ncommand_line.array\n# =\u003e [\"example.sh\", \"--opt\", \"file1.txt\", \"--opt\", \"file2.txt\"]\ncommand_line.string\n# =\u003e \"example.sh --opt file1.txt --opt file2.txt\"\n```\n\n\u003e Note: `lino` ignores `nil` or empty option values in the resulting command \n\u003e       line. \n\n#### Arguments\n\nArguments can be added using `#with_argument`:\n\n```ruby \ncommand_line = Lino.builder_for_command('diff')\n    .with_argument('./file1.txt')\n    .with_argument('./file2.txt')\n    .build\n\ncommand_line.array\n# =\u003e [\"diff\", \"./file1.txt\", \"./file2.txt\"]\ncommand_line.string\n# =\u003e \"diff ./file1.txt ./file2.txt\"\n```\n\nor `#with_arguments`, as an array:\n\n```ruby\ncommand_line = Lino.builder_for_command('diff')\n    .with_arguments(['./file1.txt', nil, '', './file2.txt'])\n    .build\n\ncommand_line.array\n# =\u003e [\"diff\", \"./file1.txt\", \"./file2.txt\"]\ncommand_line.string\n# =\u003e \"diff ./file1.txt ./file2.txt\"\n```\n\n\u003e Note: `lino` ignores `nil` or empty argument values in the resulting command \n\u003e        line.\n\n#### Option Separators\n\nBy default, when rendering command lines as a string, `lino` separates option \nvalues from the option by a space. This can be overridden globally using \n`#with_option_separator`:\n\n```ruby\ncommand_line = Lino.builder_for_command('java')\n    .with_option_separator(':')\n    .with_option('-splash', './images/splash.jpg')\n    .with_argument('./application.jar')\n    .build\n\ncommand_line.array\n# =\u003e [\"java\", \"-splash:./images/splash.jpg\", \"./application.jar\"]\ncommand_line.string\n# =\u003e \"java -splash:./images/splash.jpg ./application.jar\"\n```\n\nThe option separator can also be overridden on an option by option basis:\n\n```ruby\ncommand_line = Lino.builder_for_command('java')\n    .with_option('-splash', './images/splash.jpg', separator: ':')\n    .with_argument('./application.jar')\n    .build\n\ncommand_line.array\n# =\u003e [\"java\", \"-splash:./images/splash.jpg\", \"./application.jar\"]\ncommand_line.string\n# =\u003e \"java -splash:./images/splash.jpg ./application.jar\"\n```\n\n\u003e Note: `#with_options` supports separator overriding when the options are\n\u003e       passed as an array of hashes and a `separator` key is included in the \n\u003e       hash.\n\n\u003e Note: `#with_repeated_option` also supports the `separator` named parameter.\n\n\u003e Note: option specific separators take precedence over the global option \n\u003e       separator \n\n#### Option Quoting\n\nBy default, when rendering command line strings, `lino` does not quote option \nvalues. This can be overridden globally using `#with_option_quoting`:\n\n```ruby\ncommand_line = Lino.builder_for_command('gpg')\n    .with_option_quoting('\"')\n    .with_option('--sign', 'some file.txt')\n    .build\n\ncommand_line.string\n# =\u003e \"gpg --sign \\\"some file.txt\\\"\"\ncommand_line.array\n# =\u003e [\"gpg\", \"--sign\", \"some file.txt\"]\n```\n\nThe option quoting can also be overridden on an option by option basis:\n\n```ruby\ncommand_line = Lino.builder_for_command('java')\n    .with_option('-splash', './images/splash.jpg', quoting: '\"')\n    .with_argument('./application.jar')\n    .build\n    .string\n\ncommand_line.string\n# =\u003e \"java -splash \\\"./images/splash.jpg\\\" ./application.jar\"\ncommand_line.array\n# =\u003e [\"java\", \"-splash\", \"./images/splash.jpg\", \"./application.jar\"]\n```\n\n\u003e Note: `#with_options` supports quoting overriding when the options are\n\u003e       passed as an array of hashes and a `quoting` key is included in the \n\u003e       hash.\n\n\u003e Note: `#with_repeated_option` also supports the `quoting` named parameter.\n\n\u003e Note: option specific quoting take precedence over the global option \n\u003e       quoting \n\n\u003e Note: option quoting has no impact on the array representation of a command \n\u003e       line\n\n#### Subcommands\n\nSubcommands can be added using `#with_subcommand`:\n\n```ruby\ncommand_line = Lino.builder_for_command('git')\n    .with_flag('--no-pager')\n    .with_subcommand('log')\n    .build\n\ncommand_line.array\n# =\u003e [\"git\", \"--no-pager\", \"log\"]\ncommand_line.string\n# =\u003e \"git --no-pager log\"\n```\n\nMulti-level subcommands can be added using multiple `#with_subcommand` \ninvocations:\n\n```ruby\ncommand_line = Lino.builder_for_command('gcloud')\n    .with_subcommand('sql')\n    .with_subcommand('instances')\n    .with_subcommand('set-root-password')\n    .with_subcommand('some-database')\n    .build\n\ncommand_line.array\n# =\u003e [\"gcloud\", \"sql\", \"instances\", \"set-root-password\", \"some-database\"]\ncommand_line.string\n# =\u003e \"gcloud sql instances set-root-password some-database\"\n```\n\nor using `#with_subcommands`:\n     \n```ruby\ncommand_line = Lino.builder_for_command('gcloud')\n    .with_subcommands(\n      %w[sql instances set-root-password some-database]\n    )\n    .build\n\ncommand_line.array\n# =\u003e [\"gcloud\", \"sql\", \"instances\", \"set-root-password\", \"some-database\"]\ncommand_line.string\n# =\u003e \"gcloud sql instances set-root-password some-database\"\n```\n\nSubcommands also support options via `#with_flag`, `#with_flags`, \n`#with_option`, `#with_options` and `#with_repeated_option` just like commands,\nvia a block, for example: \n\n```ruby\ncommand_line = Lino.builder_for_command('git')\n    .with_flag('--no-pager')\n    .with_subcommand('log') do |sub|\n      sub.with_option('--since', '2016-01-01')\n    end\n    .build\n\ncommand_line.array\n# =\u003e [\"git\", \"--no-pager\", \"log\", \"--since\", \"2016-01-01\"]\ncommand_line.string\n# =\u003e \"git --no-pager log --since 2016-01-01\"\n```\n\n\u003e Note: `#with_subcommands` also supports a block, which applies in the context\n\u003e       of the last subcommand in the passed array.\n\n#### Environment Variables\n\nEnvironment variables can be added to command lines using \n`#with_environment_variable`:\n  \n```ruby\ncommand_line = Lino.builder_for_command('node')\n    .with_environment_variable('PORT', '3030')\n    .with_environment_variable('LOG_LEVEL', 'debug')\n    .with_argument('./server.js')\n    .build\n\ncommand_line.string\n# =\u003e \"PORT=\\\"3030\\\" LOG_LEVEL=\\\"debug\\\" node ./server.js\"\ncommand_line.array\n# =\u003e [\"node\", \"./server.js\"]\ncommand_line.env\n# =\u003e {\"PORT\"=\u003e\"3030\", \"LOG_LEVEL\"=\u003e\"debug\"}\n```\n\nor `#with_environment_variables`, either as a hash:\n\n```ruby\ncommand_line = Lino.builder_for_command('node')\n    .with_environment_variables({\n      'PORT' =\u003e '3030',\n      'LOG_LEVEL' =\u003e 'debug'\n    })\n    .build\n\ncommand_line.string\n# =\u003e \"PORT=\\\"3030\\\" LOG_LEVEL=\\\"debug\\\" node ./server.js\"\ncommand_line.array\n# =\u003e [\"node\", \"./server.js\"]\ncommand_line.env\n# =\u003e {\"PORT\"=\u003e\"3030\", \"LOG_LEVEL\"=\u003e\"debug\"}\n```\n\nor as an array:\n\n```ruby\ncommand_line = Lino.builder_for_command('node')\n    .with_environment_variables(\n      [\n        { name: 'PORT', value: '3030' },\n        { name: 'LOG_LEVEL', value: 'debug' }\n      ]\n    )\n    .build\n\ncommand_line.string\n# =\u003e \"PORT=\\\"3030\\\" LOG_LEVEL=\\\"debug\\\" node ./server.js\"\ncommand_line.array\n# =\u003e [\"node\", \"./server.js\"]\ncommand_line.env\n# =\u003e {\"PORT\"=\u003e\"3030\", \"LOG_LEVEL\"=\u003e\"debug\"}\n```\n\n#### Option Placement\n\nBy default, `lino` places top-level options after the command, before all \nsubcommands and arguments.\n\nThis is equivalent to calling `#with_options_after_command`:\n\n```ruby\ncommand_line = Lino.builder_for_command('gcloud')\n    .with_options_after_command\n    .with_option('--password', 'super-secure')\n    .with_subcommands(%w[sql instances set-root-password])\n    .build\n\ncommand_line.array\n# =\u003e \n# [\"gcloud\", \n#  \"--password\", \n#  \"super-secure\", \n#  \"sql\", \n#  \"instances\", \n#  \"set-root-password\"]\ncommand_line.string\n# =\u003e gcloud --password super-secure sql instances set-root-password\n```\n\nAlternatively, top-level options can be placed after all subcommands using\n`#with_options_after_subcommands`:\n\n```ruby\ncommand_line = Lino.builder_for_command('gcloud')\n    .with_options_after_subcommands\n    .with_option('--password', 'super-secure')\n    .with_subcommands(%w[sql instances set-root-password])\n    .build\n\ncommand_line.array\n# =\u003e \n# [\"gcloud\",  \n#  \"sql\", \n#  \"instances\", \n#  \"set-root-password\",\n#  \"--password\", \n#  \"super-secure\"]\ncommand_line.string\n# =\u003e gcloud sql instances set-root-password --password super-secure\n```\n\nor, after all arguments, using `#with_options_after_arguments`:\n\n```ruby\ncommand_line = Lino.builder_for_command('ls')\n    .with_options_after_arguments\n    .with_flag('-l')\n    .with_argument('/some/directory')\n    .build\n\ncommand_line.array\n# =\u003e [\"ls\", \"/some/directory\", \"-l\"]\ncommand_line.string\n# =\u003e \"ls /some/directory -l\"\n```\n\nThe option placement can be overridden on an option by option basis:\n\n```ruby\ncommand_line = Lino.builder_for_command('gcloud')\n    .with_options_after_subcommands\n    .with_option('--log-level', 'debug', placement: :after_command)\n    .with_option('--password', 'pass1')\n    .with_subcommands(%w[sql instances set-root-password])\n    .build\n\ncommand_line.array\n# =\u003e \n# [\"gcloud\", \n#  \"--log-level\", \n#  \"debug\", \n#  \"sql\", \n#  \"instances\", \n#  \"set-root-password\",\n#  \"--password\",\n#  \"pass1\"]\ncommand_line.string\n# =\u003e \"gcloud --log-level debug sql instances set-root-password --password pass1\"\n```\n\nThe `:placement` keyword argument accepts placement values of `:after_command`,\n`:after_subcommands` and `:after_arguments`.\n\n\u003e Note: `#with_options` supports placement overriding when the options are\n\u003e       passed as an array of hashes and a `placement` key is included in the\n\u003e       hash.\n\n\u003e Note: `#with_repeated_option` also supports the `placement` named parameter.\n\n\u003e Note: option specific placement take precedence over the global option\n\u003e       placement\n\n#### Appliables\n\nCommand and subcommand builders both support passing 'appliables' that are\napplied to the builder allowing an operation to be encapsulated in an object.\n\nGiven an appliable type:\n\n```ruby\nclass AppliableOption\n  def initialize(option, value)\n    @option = option\n    @value = value\n  end\n\n  def apply(builder)\n    builder.with_option(@option, @value)\n  end\nend\n```\n\nan instance of the appliable can be applied using `#with_appliable`:\n\n```ruby\ncommand_line = Lino.builder_for_command('gpg')\n    .with_appliable(AppliableOption.new('--recipient', 'tobyclemson@gmail.com'))\n    .with_flag('--sign')\n    .with_argument('/some/file.txt')\n    .build\n\ncommand_line.array\n# =\u003e [\"gpg\", \"--recipient\", \"tobyclemson@gmail.com\", \"--sign\", \"/some/file.txt\"]\ncommand_line.string\n# =\u003e \"gpg --recipient tobyclemson@gmail.com --sign /some/file.txt\" \n```\n\nor multiple with `#with_appliables`:\n\n```ruby\ncommand_line = Lino.builder_for_command('gpg')\n    .with_appliables([\n      AppliableOption.new('--recipient', 'user@example.com'),\n      AppliableOption.new('--output', '/signed.txt')\n    ])\n    .with_flag('--sign')\n    .with_argument('/file.txt')\n    .build\n\ncommand_line.array\n# =\u003e \n# [\"gpg\", \n#  \"--recipient\", \n#  \"tobyclemson@gmail.com\",\n#  \"--output\", \n#  \"/signed.txt\",\n#  \"--sign\", \n#  \"/some/file.txt\"]\ncommand_line.string\n# =\u003e \"gpg --recipient user@example.com --output /signed.txt --sign /file.txt\" \n```\n\n\u003e Note: an 'appliable' is any object that has an `#apply` method.\n\n\u003e Note: `lino` ignores `nil` or empty appliables in the resulting command line.\n\n#### Working Directory\n\nBy default, when a command line is executed, the working directory of the parent\nprocess is used. This can be overridden with `#with_working_directory`:\n\n```ruby\ncommand_line = Lino.builder_for_command('ls')\n                   .with_flag('-l')\n                   .with_working_directory('/home/tobyclemson')\n                   .build\n\ncommand_line.working_directory\n# =\u003e \"/home/tobyclemson\"\n```\n\nAll built in executors honour the provided working directory, setting it on\nspawned processes.\n\n### Executing command lines\n\n`Lino::Model::CommandLine` instances can be executed after construction. They\nutilise an executor to achieve this, which is any object that has an\n`#execute(command_line, opts)` method. `Lino` provides default executors such\nthat a custom executor only needs to be provided in special cases.\n\n#### `#execute`\n\nA `Lino::Model::CommandLine` instance can be executed using the `#execute` \nmethod:\n\n```ruby\ncommand_line = Lino.builder_for_command('ls')\n    .with_flag('-l')\n    .with_flag('-a')\n    .with_argument('/')\n    .build\n    \ncommand_line.execute\n# =\u003e \u003ccontents of / directory\u003e \n```\n\n#### Standard Streams\n\nBy default, all streams are inherited from the parent process.\n\nTo populate standard input:\n\n```ruby\nrequire 'stringio'\n\ncommand_line.execute(\n  stdin: StringIO.new('something to be passed to standard input')\n)\n```\n\nThe `stdin` option supports any object that responds to `read`.\n\nTo provide custom streams for standard output or standard error:\n\n```ruby\nrequire 'tempfile'\n  \nstdout = Tempfile.new\nstderr = Tempfile.new\n  \ncommand_line.execute(stdout: stdout, stderr: stderr)\n\nstdout.rewind\nstderr.rewind\n  \nputs \"[output: #{stdout.read}, error: #{stderr.read}]\"\n```\n\nThe `stdout` and `stderr` options support any instance of `IO` or a subclass.\n\n#### Executors\n\n`Lino` includes three built-in executors:\n\n* `Lino::Executors::Childprocess` which is based on the\n  [`childprocess` gem](https://github.com/enkessler/childprocess)\n* `Lino::Executors::Open4` which is based on the\n  [`open4` gem](https://github.com/ahoward/open4)\n* `Lino::Executors::Mock` which does not start real processes and is useful for\n  use in tests.\n\n##### Configuration\n\nBy default, an instance of `Lino::Executors::Childprocess` is used. This is\ncontrolled by the default executor configured on `Lino`:\n\n```ruby\nLino.configuration.executor\n# =\u003e #\u003cLino::Executors::Childprocess:0x0000000103007108\u003e\n\nexecutor = Lino::Executors::Mock.new\n\nLino.configure do |config|\n  config.executor = executor\nend\n\nLino.configuration.executor\n# =\u003e\n# #\u003cLino::Executors::Mock:0x0000000106d4d3c8   \n#  @executions=[],\n#  @exit_code=0,\n#  @stderr_contents=nil,\n#  @stdout_contents=nil\u003e\n\nLino.reset!\n\nLino.configuration.executor\n# =\u003e #\u003cLino::Executors::Childprocess:0x00000001090fcb48\u003e\n```\n\n##### Builder overrides\n\nAny built command will inherit the executor set as default at build time. \n\nTo override the executor on the builder, use `#with_executor`:\n\n```ruby\nexecutor = Lino::Executors::Mock.new\ncommand_line = Lino.builder_for_command('ls')\n    .with_executor(executor)\n    .build\n\ncommand_line.executor\n# =\u003e\n# #\u003cLino::Executors::Mock:0x0000000108e7d890   \n#  @executions=[],\n#  @exit_code=0,\n#  @stderr_contents=nil,\n#  @stdout_contents=nil\u003e\n```\n\n##### Mock executor\n\nThe `Lino::Executors::Mock` captures executions without spawning any real\nprocesses:\n\n```ruby\nexecutor = Lino::Executors::Mock.new\ncommand_line = Lino.builder_for_command('ls')\n    .with_executor(executor)\n    .build\n\ncommand_line.execute\n\nexecutor.executions.length\n# =\u003e 1\n\nexecution = executor.executions.first\nexecution.command_line == command_line\n# =\u003e true\nexecution.exit_code\n# =\u003e 0\n```\n\nThe mock can be configured to write to any provided `stdout` or `stderr`:\n\n```ruby\nrequire 'tempfile'\n\nexecutor = Lino::Executors::Mock.new\nexecutor.write_to_stdout('hello!')\nexecutor.write_to_stderr('error!')\n\ncommand_line = Lino.builder_for_command('ls')\n    .with_executor(executor)\n    .build\n\nstdout = Tempfile.new\nstderr = Tempfile.new\n\ncommand_line.execute(stdout:, stderr:)\n\nstdout.rewind\nstderr.rewind\n\nstdout.read == 'hello!'\n# =\u003e true\nstderr.read == 'error!'\n# =\u003e true\n```\n\nThe mock also captures any provided `stdin`:\n\n```ruby\nrequire 'stringio'\n\nexecutor = Lino::Executors::Mock.new\ncommand_line = Lino.builder_for_command('ls')\n                   .with_executor(executor)\n                   .build\n\nstdin = StringIO.new(\"input\\n\")\n\ncommand_line.execute(stdin:)\n\nexecution = executor.executions.first\nexecution.stdin_contents\n# =\u003e \"input\\n\"\n```\n\nThe mock can be configured to fail all executions:\n\n```ruby\nexecutor = Lino::Executors::Mock.new\nexecutor.fail_all_executions\n\ncommand_line = Lino.builder_for_command('ls')\n                   .with_executor(executor)\n                   .build\n\ncommand_line.execute\n# ...in `execute': Failed while executing command line. \n# (Lino::Errors::ExecutionError)\n\ncommand_line.execute\n# ...in `execute': Failed while executing command line. \n# (Lino::Errors::ExecutionError)\n```\n\nThe exit code, which defaults to zero, can also be set explicitly, with anything\nother than zero causing a `Lino::Errors::ExecutionError` to be raised:\n\n```ruby\nexecutor = Lino::Executors::Mock.new\nexecutor.exit_code = 128\n\ncommand_line = Lino.builder_for_command('ls')\n                   .with_executor(executor)\n                   .build\n\nbegin\n  command_line.execute\nrescue Lino::Errors::ExecutionError =\u003e e\n  e.exit_code\nend\n# =\u003e 128\n```\n\nThe mock is stateful and accumulates executions and configurations. To reset the\nmock to its initial state:\n\n```ruby\nexecutor = Lino::Executors::Mock.new\nexecutor.exit_code = 128\nexecutor.write_to_stdout('hello!')\nexecutor.write_to_stderr('error!')\n\nexecutor.reset\n\nexecutor.exit_code\n# =\u003e 0\nexecutor.stdout_contents\n# =\u003e nil\nexecutor.stderr_contents\n# =\u003e nil\n```\n\n## Development\n\nTo install dependencies and run the build, run the pre-commit build:\n\n```shell script\n./go\n```\n\nThis runs all unit tests and other checks including coverage and code linting / \nformatting.\n\nTo run only the unit tests, including coverage:\n\n```shell script\n./go test:unit\n```\n\nTo attempt to fix any code linting / formatting issues:\n\n```shell script\n./go library:fix\n```\n\nTo check for code linting / formatting issues without fixing:\n\n```shell script\n./go library:check\n```\n\nYou can also run `bin/console` for an interactive prompt that will allow you to \nexperiment.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at \nhttps://github.com/infrablocks/lino. This project is intended to be a safe, \nwelcoming space for collaboration, and contributors are expected to adhere to \nthe [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n## License\n\nThe gem is available as open source under the terms of the \n[MIT License](http://opensource.org/licenses/MIT).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfrablocks%2Flino","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finfrablocks%2Flino","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfrablocks%2Flino/lists"}