bake-0.25.0/0000755000004100000410000000000015216537407012542 5ustar www-datawww-databake-0.25.0/bin/0000755000004100000410000000000015216537407013312 5ustar www-datawww-databake-0.25.0/bin/bake0000755000004100000410000000255615216537407014152 0ustar www-datawww-data#!/usr/bin/env ruby # frozen_string_literal: true # Copyright, 2020, by Samuel G. D. Williams. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # This avoids the need to use `bundle exec bake`: require "bundler/setup" require_relative "../lib/bake/command" begin Bake::Command.call rescue => error Console.error(Bake::Command, error) exit 1 end bake-0.25.0/lib/0000755000004100000410000000000015216537407013310 5ustar www-datawww-databake-0.25.0/lib/bake.rb0000644000004100000410000000035615216537407014543 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "bake/version" require_relative "bake/registry" require_relative "bake/context" require_relative "bake/format" bake-0.25.0/lib/bake/0000755000004100000410000000000015216537407014212 5ustar www-datawww-databake-0.25.0/lib/bake/arguments.rb0000644000004100000410000000441715216537407016552 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "type" require_relative "documentation" module Bake # Structured access to arguments. class Arguments def self.extract(recipe, arguments, **defaults) # Only supply defaults that match the recipe option names: defaults = defaults.slice(*recipe.required_options) self.new(recipe, defaults).extract(arguments) end def initialize(recipe, defaults) @recipe = recipe @types = recipe.types @parameters = recipe.parameters @arity = recipe.arity @ordered = [] @options = defaults end attr :ordered attr :options def extract(arguments) while argument = arguments.first if /^--(?.*)$/ =~ argument # Consume the argument: arguments.shift if name.empty? break end name = normalize(name) # Extract the trailing arguments: @options[name] = extract_arguments(name, arguments) elsif @ordered.size < @arity _, name = @parameters.shift value = arguments.shift # Consume it: @ordered << extract_argument(name, value) elsif /^(?.*?)=(?.*)$/ =~ argument # Consume the argument: arguments.shift name = name.to_sym # Extract the single argument: @options[name] = extract_argument(name, value) else break end end return @ordered, @options end private def normalize(name) name.tr("-", "_").to_sym end def delimiter_index(arguments) arguments.index{|argument| argument =~ /\A(--|;\z)/} end def extract_arguments(name, arguments) value = nil type = @types[name] # Can this named parameter accept more than one input argument? if type&.composite? if count = delimiter_index(arguments) value = arguments.shift(count) arguments.shift if arguments.first == ";" else value = arguments.dup arguments.clear end else # Otherwise we just take one item: value = arguments.shift end if type value = type.parse(value) end return value end def extract_argument(name, value) if type = @types[name] value = type.parse(value) end return value end end end bake-0.25.0/lib/bake/type/0000755000004100000410000000000015216537407015173 5ustar www-datawww-databake-0.25.0/lib/bake/type/symbol.rb0000644000004100000410000000045615216537407017032 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "any" module Bake module Type module Symbol extend Type def self.composite? false end def self.parse(input) input.to_sym end end end end bake-0.25.0/lib/bake/type/output.rb0000644000004100000410000000064115216537407017061 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "any" module Bake module Type module Output extend Type def self.composite? false end def self.parse(input) case input when "-" return $stdout when IO, StringIO return input else File.open(input, "w") end end end end end bake-0.25.0/lib/bake/type/input.rb0000644000004100000410000000063215216537407016660 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "any" module Bake module Type module Input extend Type def self.composite? false end def self.parse(input) case input when "-" return $stdin when IO, StringIO return input else File.open(input) end end end end end bake-0.25.0/lib/bake/type/float.rb0000644000004100000410000000045315216537407016627 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "any" module Bake module Type module Float extend Type def self.composite? false end def self.parse(input) input.to_f end end end end bake-0.25.0/lib/bake/type/integer.rb0000644000004100000410000000046115216537407017156 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "any" module Bake module Type module Integer extend Type def self.composite? false end def self.parse(input) Integer(input) end end end end bake-0.25.0/lib/bake/type/string.rb0000644000004100000410000000045415216537407017031 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "any" module Bake module Type module String extend Type def self.composite? false end def self.parse(input) input.to_s end end end end bake-0.25.0/lib/bake/type/hash.rb0000644000004100000410000000125415216537407016445 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "any" module Bake module Type class Hash include Type def initialize(key_type, value_type) @key_type = key_type @value_type = value_type end def composite? true end def parse(input) hash = {} input.split(",").each do |pair| key, value = pair.split(":", 2) key = @key_type.parse(key) value = @value_type.parse(value) hash[key] = value end return hash end end def self.Hash(key_type, value_type) Hash.new(key_type, value_type) end end end bake-0.25.0/lib/bake/type/nil.rb0000644000004100000410000000063415216537407016305 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "any" module Bake module Type module Nil extend Type def self.composite? false end def self.parse(input) if input =~ /nil|null/i return nil else raise ArgumentError, "Cannot coerce #{input.inspect} into nil!" end end end end end bake-0.25.0/lib/bake/type/decimal.rb0000644000004100000410000000051215216537407017114 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "any" require "bigdecimal" module Bake module Type module Decimal extend Type def self.composite? false end def self.parse(input) BigDecimal(input) end end end end bake-0.25.0/lib/bake/type/boolean.rb0000644000004100000410000000074215216537407017142 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "any" module Bake module Type module Boolean extend Type def self.composite? false end def self.parse(input) if input =~ /t(rue)?|y(es)?/i return true elsif input =~ /f(alse)?|n(o)?/i return false else raise ArgumentError, "Cannot coerce #{input.inspect} into boolean!" end end end end end bake-0.25.0/lib/bake/type/tuple.rb0000644000004100000410000000133615216537407016654 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "any" module Bake module Type class Tuple include Type def initialize(item_types) @item_types = item_types end def composite? true end def parse(input) case input when ::String return input.split(",").map{|value| @item_type.parse(value)} when ::Array return input.map{|value| @item_type.parse(value)} else raise ArgumentError, "Cannot coerce #{input.inspect} into tuple!" end end def to_s "a Tuple of (#{@item_types.join(', ')})" end end def self.Tuple(*item_types) Tuple.new(item_types) end end end bake-0.25.0/lib/bake/type/any.rb0000644000004100000410000000350615216537407016313 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. module Bake module Type # An ordered list of types. The first type to match the input is used. # # ```ruby # type = Bake::Type::Any(Bake::Type::String, Bake::Type::Integer) # ``` # class Any # Initialize the instance with an array of types. # @parameter types [Array] the array of types. def initialize(types) @types = types end # Create a copy of the current instance with the other type appended. # @parameter other [Type] the type instance to append. def | other self.class.new([*@types, other]) end # Whether any of the listed types is a composite type. # @returns [Boolean] true if any of the listed types is `composite?`. def composite? @types.any?{|type| type.composite?} end # Parse an input string, trying the listed types in order, returning the first one which doesn't raise an exception. # @parameter input [String] the input to parse, e.g. `"5"`. def parse(input) @types.each do |type| return type.parse(input) rescue # Ignore. end end # As a class type, accepts any value. def self.parse(value) value end # Generate a readable string representation of the listed types. def to_s "any of #{@types.join(', ')}" end end # An extension module which allows constructing `Any` types using the `|` operator. module Type # Create an instance of `Any` with the arguments as types. # @parameter other [Type] the alternative type to match. def | other Any.new([self, other]) end end # A type constructor. # # ```ruby # Any(Integer, String) # ``` # # See [Any.initialize](#Bake::Type::Any::initialize). # def self.Any(*types) Any.new(types) end end end bake-0.25.0/lib/bake/type/array.rb0000644000004100000410000000143715216537407016643 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "any" module Bake module Type class Array include Type def initialize(item_type) @item_type = item_type end def composite? true end def map(values) values.map{|value| @item_type.parse(value)} end def parse(input) case input when ::String return input.split(",").map{|value| @item_type.parse(value)} when ::Array return input.map{|value| @item_type.parse(value)} else raise ArgumentError, "Cannot coerce #{input.inspect} into array!" end end def to_s "an Array of #{@item_type}" end end def self.Array(item_type = Any) Array.new(item_type) end end end bake-0.25.0/lib/bake/format/0000755000004100000410000000000015216537407015502 5ustar www-datawww-databake-0.25.0/lib/bake/format/yaml.rb0000644000004100000410000000051215216537407016767 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2025, by Samuel Williams. require "yaml" module Bake module Format module YAML def self.input(file) ::YAML.load(file) end def self.output(file, value) ::YAML.dump(value, file) end end REGISTRY[:yaml] = YAML end end bake-0.25.0/lib/bake/format/raw.rb0000644000004100000410000000046215216537407016622 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2025, by Samuel Williams. require "pp" module Bake module Format module Raw def self.input(file) file end def self.output(file, value) PP.pp(value, file) end end REGISTRY[:raw] = Raw end end bake-0.25.0/lib/bake/format/json.rb0000644000004100000410000000071315216537407017001 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2025, by Samuel Williams. require "json" module Bake module Format module JSON def self.input(file) ::JSON.load(file) end OPTIONS = {indent: " ", space: " ", space_before: "", object_nl: "\n", array_nl: "\n"} def self.output(file, value) ::JSON::State.generate(value, OPTIONS, file) file.puts end end REGISTRY[:json] = JSON end end bake-0.25.0/lib/bake/format/ndjson.rb0000644000004100000410000000107215216537407017322 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2025, by Samuel Williams. require "json" module Bake module Format class NDJSON def self.input(file) new(file) end def self.output(file, value) value.each do |item| file.puts(JSON.generate(item)) end end def initialize(file) @file = file end attr :file def each return to_enum unless block_given? @file.each_line do |line| yield JSON.parse(line) end end end REGISTRY[:ndjson] = NDJSON end end bake-0.25.0/lib/bake/base.rb0000644000004100000410000000400515216537407015450 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "recipe" require_relative "scope" module Bake # The base class for including {Scope} instances which define {Recipe} instances. class Base < Struct.new(:context) # Generate a base class for the specified path. # @parameter path [Array(String)] The command path. def self.derive(path = []) klass = Class.new(self) klass.const_set(:PATH, path) return klass end # Format the class as a command. # @returns [String] def self.to_s if path = self.path path.join(":") else super end end def self.inspect if path = self.path "Bake::Base[#{path.join(':')}]" else super end end # The path of this derived base class. # @returns [Array(String)] def self.path self.const_get(:PATH) rescue nil end # If an instance generates output, it should override this method to return `true`, otherwise default output handling will be used (essentially the return value of the last recipe). # # @returns [Boolean] Whether this instance handles its own output. def output?(recipe) false end # The path for this derived base class. # @returns [Array(String)] def path self.class.path end # Proxy a method call using command line arguments through to the {Context} instance. # @parameter arguments [Array(String)] def call(*arguments) self.context.call(*arguments) end # Recipes defined in this scope. # # @yields {|recipe| ...} # @parameter recipe [Recipe] # @returns [Enumerable] def recipes return to_enum(:recipes) unless block_given? names = self.public_methods - Base.public_instance_methods names.each do |name| yield recipe_for(name) end end # Look up a recipe with a specific name. # # @parameter name [String] The instance method to look up. def recipe_for(name) Recipe.new(self, name) end def to_s "\#<#{self.class}>" end end end bake-0.25.0/lib/bake/command.rb0000644000004100000410000000041415216537407016154 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "command/top" module Bake # The command line interface. module Command def self.call(*arguments) Top.call(*arguments) end end end bake-0.25.0/lib/bake/context.rb0000644000004100000410000001064715216537407016233 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. # Copyright, 2020, by Olle Jonsson. require_relative "base" require_relative "registry" module Bake # The default file name for the top level bakefile. BAKEFILE = "bake.rb" # Represents a context of task execution, containing all relevant state. class Context # Search upwards from the specified path for a {BAKEFILE}. # If path points to a file, assume it's a `bake.rb` file. Otherwise, recursively search up the directory tree starting from `path` to find the specified bakefile. # @returns [String | Nil] The path to the bakefile if it could be found. def self.bakefile_path(path, bakefile: BAKEFILE) if File.file?(path) return path end current = path while current bakefile_path = File.join(current, BAKEFILE) if File.exist?(bakefile_path) return bakefile_path end parent = File.dirname(current) if current == parent break else current = parent end end return nil end # Load a context from the specified path. # @path [String] A file-system path. def self.load(path = Dir.pwd) if bakefile_path = self.bakefile_path(path) working_directory = File.dirname(bakefile_path) else working_directory = path end registry = Registry.default(working_directory, bakefile_path) context = self.new(registry, working_directory) context.bakefile return context end # Initialize the context with the specified registry. # @parameter registry [Registry] def initialize(registry, root = nil) @registry = registry @root = root @instances = Hash.new do |hash, key| hash[key] = instance_for(key) end @recipes = Hash.new do |hash, key| hash[key] = recipe_for(key) end end def bakefile @instances[[]] end # The registry which will be used to resolve recipes in this context. attr :registry # The root path of this context. # @returns [String | Nil] attr :root # Invoke recipes on the context using command line arguments. # # e.g. `context.call("gem:release:version:increment", "0,0,1")` # # @parameter commands [Array(String)] # @yield {|recipe, result| If a block is given, it will be called with the last recipe and its result. # @parameter recipe [Recipe] The last recipe that was called. # @parameter result [Object | Nil] The result of the last recipe. # @returns [Object] The result of the last recipe. def call(*commands, &block) recipe = nil last_result = nil # Invoke the recipes in the order they were specified: while command = commands.shift if recipe = @recipes[command] arguments, options = recipe.prepare(commands, last_result) last_result = recipe.call(*arguments, **options) else raise ArgumentError, "Could not find recipe for #{command}!" end end # If a block is given, we yield the last recipe and its result: if block_given? yield recipe, last_result end return last_result end # Lookup a recipe for the given command name. # @parameter command [String] The command name, e.g. `bundler:release`. def lookup(command) @recipes[command] end alias [] lookup def to_s if @root "#{self.class} #{File.basename(@root)}" else self.class.name end end def inspect "\#<#{self.class} #{@root}>" end private def recipe_for(command) path = command.split(":") # If the command is in the form `foo:bar`, we check two cases: # # (1) We check for an instance at path `foo:bar` and if it responds to `bar`. if instance = @instances[path] and instance.respond_to?(path.last) return instance.recipe_for(path.last) else # (2) We check for an instance at path `foo` and if it responds to `bar`. *path, name = *path if instance = @instances[path] and instance.respond_to?(name) return instance.recipe_for(name) end end return nil end def instance_for(path) if base = base_for(path) return base.new(self) end end # @parameter path [Array(String)] the path for the scope. def base_for(path) base = nil # For each loader, we check if it has a scope for the given path. If it does, we prepend it to the base: @registry.scopes_for(path) do |scope| base ||= Base.derive(path) base.prepend(scope) end return base end end end bake-0.25.0/lib/bake/version.rb0000644000004100000410000000022215216537407016220 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. module Bake VERSION = "0.25.0" end bake-0.25.0/lib/bake/command/0000755000004100000410000000000015216537407015630 5ustar www-datawww-databake-0.25.0/lib/bake/command/top.rb0000644000004100000410000000277715216537407016774 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require "samovar" require "console/terminal" require_relative "call" require_relative "list" module Bake module Command # The top level command line application. class Top < Samovar::Command self.description = "Execute tasks using Ruby." options do option "-h/--help", "Show help." option "-b/--bakefile ", "Override the path to the bakefile to use." option "-g/--gem ", 'Load the specified gem, e.g. "bake ~> 1.0".' do |value| gem(*value.split(/\s+/)) end end nested :command, { "call" => Call, "list" => List, }, default: "call" def terminal(output = self.output) terminal = Console::Terminal.for(output) terminal[:context] = terminal[:loader] = terminal.style(nil, nil, :bold) terminal[:command] = terminal.style(nil, nil, :bold) terminal[:description] = terminal.style(:blue) terminal[:key] = terminal[:opt] = terminal.style(:green) terminal[:req] = terminal.style(:red) terminal[:keyreq] = terminal.style(:red, nil, :bold) terminal[:keyrest] = terminal.style(:green) terminal[:parameter] = terminal[:opt] return terminal end def bakefile_path @options[:bakefile] || Dir.pwd end def context Context.load(self.bakefile_path) end def call if @options[:help] self.print_usage else @command.call end end end end end bake-0.25.0/lib/bake/command/call.rb0000644000004100000410000000150015216537407017064 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require "samovar" require_relative "../registry" require_relative "../context" module Bake module Command # Execute one or more commands. class Call < Samovar::Command self.description = "Execute one or more commands." def bakefile @parent.bakefile end many :commands, "The commands & arguments to invoke.", default: ["default"], stop: false def format(output, value) if formatter = OUTPUT[output] formatter.call(value) end end def call context = @parent.context context.call(*@commands) do |recipe, last_result| if last_result and !recipe.output? context.lookup("output").call(input: last_result) end end end end end end bake-0.25.0/lib/bake/command/list.rb0000644000004100000410000000422015216537407017126 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require "samovar" require "set" module Bake module Command # List all available commands. class List < Samovar::Command self.description = "List all available commands." one :pattern, "The pattern to filter tasks by." def format_parameters(parameters, terminal) parameters.each do |type, name| case type when :key name = "#{name}=" when :keyreq name = "#{name}=" when :keyrest name = "**#{name}" else name = name.to_s end terminal.print(:reset, " ") terminal.print(type, name) end end def format_recipe(recipe, terminal) terminal.print(:command, recipe.command) if parameters = recipe.parameters format_parameters(parameters, terminal) end end def print_scope(terminal, scope, printed: false) format_recipe = self.method(:format_recipe).curry scope.recipes.sort.each do |recipe| if @pattern and !recipe.command.include?(pattern) next end unless printed yield printed = true end terminal.print_line terminal.print_line("\t", format_recipe[recipe]) documentation = recipe.documentation documentation.description do |line| terminal.print_line("\t\t", :description, line) end documentation.parameters do |parameter| terminal.print_line("\t\t", :parameter, parameter[:name], :reset, " [", :type, parameter[:type], :reset, "] ", :description, parameter[:details] ) end end return printed end def call first = true terminal = @parent.terminal context = @parent.context context.registry.each do |loader| printed = false loader.each do |path| loader.scopes_for(path) do |scope| print_scope(terminal, scope, printed: printed) do terminal.print_line(:loader, loader) printed = true end end end if printed terminal.print_line end end end end end end bake-0.25.0/lib/bake/documentation.rb0000644000004100000410000000414415216537407017413 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "scope" module Bake # Structured access to a set of comment lines. class Documentation # Initialize the documentation with an array of comments. # # @parameter comments [Array(String)] An array of comment lines. def initialize(comments) @comments = comments end DESCRIPTION = /\A\s*([^@\s].*)?\z/ # The text-only lines of the comment block. # # @yields {|match| ...} # @parameter match [MatchData] The regular expression match for each line of documentation. # @returns [Enumerable] If no block given. def description return to_enum(:description) unless block_given? # We track empty lines and only yield a single empty line when there is another line of text: gap = false @comments.each do |comment| if match = comment.match(DESCRIPTION) if match[1] if gap yield "" gap = false end yield match[1] else gap = true end else break end end end ATTRIBUTE = /\A\s*@(?.*?)\s+(?.*?)\z/ # The attribute lines of the comment block. # e.g. `@returns [String]`. # # @yields {|match| ...} # @parameter match [MatchData] The regular expression match with `name` and `value` keys. # @returns [Enumerable] If no block given. def attributes return to_enum(:attributes) unless block_given? @comments.each do |comment| if match = comment.match(ATTRIBUTE) yield match end end end PARAMETER = /\A@param(eter)?\s+(?.*?)\s+\[(?.*?)\](\s+(?
.*?))?\z/ # The parameter lines of the comment block. # e.g. `@parameter value [String] The value.` # # @yields {|match| ...} # @parameter match [MatchData] The regular expression match with `name`, `type` and `details` keys. # @returns [Enumerable] If no block given. def parameters return to_enum(:parameters) unless block_given? @comments.each do |comment| if match = comment.match(PARAMETER) yield match end end end end end bake-0.25.0/lib/bake/recipe.rb0000644000004100000410000001136615216537407016015 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "type" require_relative "arguments" require_relative "documentation" module Bake # Structured access to an instance method in a bakefile. class Recipe # Initialize the recipe. # # @parameter instance [Base] The instance this recipe is attached to. # @parameter name [String] The method name. # @parameter method [Method | Nil] The method if already known. def initialize(instance, name, method = nil) @instance = instance @name = name @command = nil @comments = nil @signature = nil @documentation = nil @method = method @arity = nil end # The {Base} instance that this recipe is attached to. attr :instance # The name of this recipe. attr :name # Sort by location in source file. def <=> other (self.source_location || []) <=> (other.source_location || []) end # The method implementation. def method @method ||= @instance.method(@name) end # The source location of this recipe. def source_location self.method.source_location end # The recipe's formal parameters, if any. # @returns [Array | Nil] def parameters parameters = method.parameters unless parameters.empty? return parameters end end # Whether this recipe has optional arguments. # @returns [Boolean] def options? if parameters = self.parameters parameters.any? do |type, name| type == :keyrest || type == :keyreq || type == :key end end end # If a recipe produces output, we do not need to invoke the default output command. # @returns [Boolean] Whether this recipe produces output. def output? @instance.output?(self) end def required_options if parameters = self.parameters parameters.map do |(type, name)| if type == :keyreq name end end.compact end end # The command name for this recipe. def command @command ||= compute_command end def to_s self.command end # The method's arity, the required number of positional arguments. def arity if @arity.nil? @arity = method.parameters.count{|type, name| type == :req} end return @arity end # Process command line arguments into the ordered and optional arguments. # @parameter arguments [Array(String)] The command line arguments # @returns ordered [Array] # @returns options [Hash] def prepare(arguments, last_result = nil) Arguments.extract(self, arguments, input: last_result) end # Call the recipe with the specified arguments and options. # If the recipe does not accept options, they will be ignored. def call(*arguments, **options, &block) if options.any? and self.options? @instance.send(@name, *arguments, **options, &block) else # Ignore options... @instance.send(@name, *arguments, &block) end end # Any comments associated with the source code which defined the method. # @returns [Array(String)] The comment lines. def comments @comments ||= read_comments end # The documentation object which provides structured access to the {comments}. # @returns [Documentation] def documentation @documentation ||= Documentation.new(self.comments) end # The documented type signature of the recipe. # @returns [Array] An array of {Type} instances. def signature @signature ||= read_signature end # @deprecated Use {signature} instead. alias types signature private def parse(name, value, arguments, types) if count = arguments.index(";") value = arguments.shift(count) arguments.shift end if type = types[name] value = type.parse(value) end return value end def compute_command path = @instance.path if path.empty? @name.to_s elsif path.last.to_sym == @name path.join(":") else (path + [@name]).join(":") end end COMMENT = /\A\s*\#\s?(.*?)\Z/ def read_comments unless source_location = self.method&.source_location # Bail early if we don't have a source location (there are some inconsequential cases on JRuby): return [] end file, line_number = source_location lines = File.readlines(file) line_index = line_number - 1 description = [] line_index -= 1 # Extract comment preceeding method: while line = lines[line_index] # \Z matches a trailing newline: if match = line.match(COMMENT) description.unshift(match[1]) else break end line_index -= 1 end return description end def read_signature types = {} self.documentation.parameters do |parameter| types[parameter[:name].to_sym] = Type.parse(parameter[:type]) end return types end end end bake-0.25.0/lib/bake/registry.rb0000644000004100000410000000051015216537407016403 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2024-2025, by Samuel Williams. require_relative "registry/aggregate" module Bake # Structured access to the working directory and loaded gems for loading bakefiles. module Registry def self.default(...) Aggregate.default(...) end end end bake-0.25.0/lib/bake/scope.rb0000644000004100000410000000270515216537407015654 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "recipe" module Bake # Used for containing all methods defined in a bakefile. module Scope # Load the specified file into a unique scope module, which can then be included into a {Base} instance. def self.load(file_path, path = []) scope = Module.new if scope.respond_to?(:set_temporary_name) scope.set_temporary_name("#{self.name}[#{file_path}]") end scope.extend(self) scope.const_set(:FILE_PATH, file_path) scope.const_set(:PATH, path) # yield scope if block_given? scope.module_eval(File.read(file_path), file_path) return scope end # Recipes defined in this scope. # # @yields {|recipe| ...} # @parameter recipe [Recipe] # @returns [Enumerable] def recipes return to_enum(:recipes) unless block_given? names = self.instance_methods names.each do |name| yield recipe_for(name) end end # The path of the file that was used to {load} this scope. def file_path pp file_path_self: self self.const_get(:FILE_PATH) end # The path of the scope, relative to the root of the context. def path self.const_get(:PATH) end # Look up a recipe with a specific name. # # @parameter name [String] The instance method to look up. def recipe_for(name) Recipe.new(self, name, self.instance_method(name)) end end end bake-0.25.0/lib/bake/type.rb0000644000004100000410000000113115216537407015514 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "type/any" require_relative "type/array" require_relative "type/boolean" require_relative "type/decimal" require_relative "type/float" require_relative "type/hash" require_relative "type/input" require_relative "type/integer" require_relative "type/nil" require_relative "type/output" require_relative "type/string" require_relative "type/symbol" require_relative "type/tuple" module Bake module Type def self.parse(signature) eval(signature, binding) end end end bake-0.25.0/lib/bake/format.rb0000644000004100000410000000070615216537407016032 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2025, by Samuel Williams. module Bake module Format REGISTRY = {} def self.[](name) unless name =~ /\A[a-z_]+\z/ raise ArgumentError.new("Invalid format name: #{name}") end begin require_relative "format/#{name}" rescue LoadError raise ArgumentError.new("Unknown format: #{name}") end return REGISTRY[name.to_sym] end end end bake-0.25.0/lib/bake/registry/0000755000004100000410000000000015216537407016062 5ustar www-datawww-databake-0.25.0/lib/bake/registry/aggregate.rb0000644000004100000410000000520315216537407020335 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require "console" require_relative "directory_loader" require_relative "bakefile_loader" module Bake # Structured access to the working directory and loaded gems for loading bakefiles. module Registry class Aggregate include Enumerable # Create a loader using the specified working directory. # @parameter working_directory [String] def self.default(working_directory, bakefile_path = nil) registry = self.new if bakefile_path registry.append_bakefile(bakefile_path) end registry.append_defaults(working_directory) return registry end # Initialize an empty array of registry. def initialize # Used to de-duplicated directories: @roots = {} # The ordered list of loaders: @ordered = Array.new end # Whether any registry are defined. # @returns [Boolean] def empty? @ordered.empty? end # Enumerate the registry in order. def each(&block) @ordered.each(&block) end def scopes_for(path, &block) @ordered.each do |registry| registry.scopes_for(path, &block) end end def append_bakefile(path) @ordered << BakefileLoader.new(path) end # Append a specific project path to the search path for recipes. # The computed path will have `bake` appended to it. # @parameter current [String] The path to add. def append_path(current = Dir.pwd, **options) bake_path = File.join(current, "bake") if File.directory?(bake_path) return insert(bake_path, **options) end return false end # Add registry according to the current working directory and loaded gems. # @parameter working_directory [String] def append_defaults(working_directory) # Load recipes from working directory: self.append_path(working_directory) # Load recipes from loaded gems: self.append_from_gems end # Enumerate all loaded gems and add them. def append_from_gems ::Gem.loaded_specs.each do |name, spec| Console.debug(self){"Checking gem #{name}: #{spec.full_gem_path}..."} if path = spec.full_gem_path and File.directory?(path) append_path(path, name: spec.full_name) end end end protected def insert(directory, **options) unless @roots.key?(directory) Console.debug(self){"Adding #{directory.inspect}"} loader = DirectoryLoader.new(directory, **options) @roots[directory] = loader @ordered << loader return true end return false end end end end bake-0.25.0/lib/bake/registry/bakefile_loader.rb0000644000004100000410000000071615216537407021503 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2024-2025, by Samuel Williams. require_relative "../scope" module Bake module Registry class BakefileLoader def initialize(path) @path = path end def to_s "#{self.class} #{@path}" end attr :path def each(&block) yield [] end def scopes_for(path) if path == [] yield Scope.load(@path, []) end end end end end bake-0.25.0/lib/bake/registry/directory_loader.rb0000644000004100000410000000325115216537407021742 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2025, by Samuel Williams. require_relative "../scope" module Bake module Registry # Represents a directory which contains bakefiles. class DirectoryLoader # Initialize the loader with the specified root path. # @parameter root [String] A file-system path. def initialize(root, name: nil) @root = root @name = name end def to_s "#{self.class} #{@name || @root}" end # The root path for this loader. attr :root # Enumerate all bakefiles within the loaders root directory. # # You can pass the yielded path to {scope_for} to load the corresponding {Scope}. # # @yields {|path| ...} # @parameter path [String] The (relative) scope path. def each return to_enum unless block_given? Dir.glob("**/*.rb", base: @root) do |file_path| yield file_path.sub(/\.rb$/, "").split(File::SEPARATOR) end end # Load the {Scope} for the specified relative path within this loader, if it exists. # @parameter path [Array(String)] A relative path. def scopes_for(path) *directory, file = *path file_path = File.join(@root, directory, "#{file}.rb") if File.exist?(file_path) yield Scope.load(file_path, path) end end end class FileLoader def initialize(paths) @paths = paths end def to_s "#{self.class} #{@paths}" end attr :paths def each(&block) @paths.each_key(&block) end def scope_for(path) if file_path = @paths[path] if File.exist?(file_path) return Scope.load(file_path, path) end end end end end end bake-0.25.0/checksums.yaml.gz.sig0000444000004100000410000000060015216537407016605 0ustar www-datawww-datafZ:|#Q/VMrfu`_e(&aAԀe*F{-K^ o/ɵp`DaBztGP BCcih~jI"ǩۭkCRebF:7r_iNN kyKJ> E:;VPg%$R0ڭJ_WlQ{il$hT!ȸɘPC]V*A]υF #FYMfXZcx7 #&(nkj@Z 狖rci,DMo>{09?p7C4bake-0.25.0/readme.md0000644000004100000410000001027515216537407014326 0ustar www-datawww-data# ![Bake](logo.svg) Bake is a task execution tool, inspired by Rake, but codifying many of the use cases which are typically implemented in an ad-hoc manner. [![Development Status](https://github.com/ioquatix/bake/workflows/Test/badge.svg)](https://github.com/ioquatix/bake/actions?workflow=Test) ## Features Rake is an awesome tool and loved by the community. So, why reinvent it? Bake provides the following features that Rake does not: - On demand loading of files following a standard convention. This avoid loading all your rake tasks just to execute a single command. - Better argument handling including support for positional and optional arguments. - Focused on task execution not dependency resolution. Implementation is simpler and a bit more predictable. - Canonical structure for integration with gems. That being said, Rake and Bake can exist side by side in the same project. ## Usage Please see the [project documentation](https://ioquatix.github.io/bake/) for more details. - [Getting Started](https://ioquatix.github.io/bake/guides/getting-started/index) - This guide gives a general overview of `bake` and how to use it. - [Command Line Interface](https://ioquatix.github.io/bake/guides/command-line-interface/index) - The `bake` command is broken up into two main functions: `list` and `call`. - [Project Integration](https://ioquatix.github.io/bake/guides/project-integration/index) - This guide explains how to add `bake` to a Ruby project. - [Gem Integration](https://ioquatix.github.io/bake/guides/gem-integration/index) - This guide explains how to add `bake` to a Ruby gem and export standardised tasks for use by other gems and projects. - [Input and Output](https://ioquatix.github.io/bake/guides/input-and-output/index) - `bake` has built in tasks for reading input and writing output in different formats. While this can be useful for general processing, there are some limitations, notably that rich object representations like `json` and `yaml` often don't support stream processing. ## Releases Please see the [project releases](https://ioquatix.github.io/bake/releases/index) for all releases. ### v0.25.0 - Prefer positional arguments before interpreting `name=value` optional arguments. ### v0.24.1 - Add agent context. ### v0.24.0 - If the final result of a recipe is not an `output?`, it will now be passed to the default output recipe. ### v0.23.0 - Add support for `ndjson`. - General improvements to input and output handling. - Removed support for `pp` output - use `raw` if required. ## Contributing We welcome contributions to this project. 1. Fork it. 2. Create your feature branch (`git checkout -b my-new-feature`). 3. Commit your changes (`git commit -am 'Add some feature'`). 4. Push to the branch (`git push origin my-new-feature`). 5. Create new Pull Request. ### Running Tests To run the test suite: ``` shell bundle exec sus ``` ### Making Releases To make a new release: ``` shell bundle exec bake gem:release:patch # or minor or major ``` ### Developer Certificate of Origin In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed. ### Community Guidelines This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers. ## See Also - [Bake::Gem](https://github.com/ioquatix/bake-gem) — Release and install gems using `bake`. - [Bake::Modernize](https://github.com/ioquatix/bake-modernize) — Modernize gems consistently using `bake`. - [Console](https://github.com/socketry/console) — A logging framework which integrates with `bake`. - [Variant](https://github.com/socketry/variant) — A framework for selecting different environments, including `bake` tasks. - [Utopia](https://github.com/socketry/utopia) — A website framework which uses `bake` for maintenance tasks. bake-0.25.0/data.tar.gz.sig0000444000004100000410000000060015216537407015355 0ustar www-datawww-dataStZ [nk"a !4VߒWC:8M a[↜G?XP].Xe[a}n;[|BTPrgF @2oR1C1%]Qa"\*Zz>};$3oqЂz 㓓7VUVH+ƀӭl,@Pl8׾ቅ+(>ہ{/o]6X)iw6WI!_,~͑G_B_vohiO:;oG^[K"iI;S ̻)ډ^j٤OOU$[vAH!{bake-0.25.0/bake.gemspec0000644000004100000410000001072715216537407015020 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: bake 0.25.0 ruby lib Gem::Specification.new do |s| s.name = "bake".freeze s.version = "0.25.0".freeze s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "documentation_uri" => "https://ioquatix.github.io/bake/", "funding_uri" => "https://github.com/sponsors/ioquatix/", "source_code_uri" => "https://github.com/ioquatix/bake.git" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Samuel Williams".freeze, "Olle Jonsson".freeze] s.cert_chain = ["-----BEGIN CERTIFICATE-----\nMIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11\nZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK\nCZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz\nMjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd\nMBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj\nbzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB\nigKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2\n9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW\nsGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE\ne5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN\nXibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss\nRZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn\ntUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM\nzp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW\nxm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O\nBBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs\naWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs\naWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE\ncBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl\nxCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/\nc1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp\n8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws\nJkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP\neX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt\nQ2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8\nvoD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=\n-----END CERTIFICATE-----\n".freeze] s.date = "1980-01-02" s.executables = ["bake".freeze] s.files = ["bake/input.rb".freeze, "bake/output.rb".freeze, "bin/bake".freeze, "context/command-line-interface.md".freeze, "context/gem-integration.md".freeze, "context/getting-started.md".freeze, "context/index.yaml".freeze, "context/input-and-output.md".freeze, "context/project-integration.md".freeze, "lib/bake.rb".freeze, "lib/bake/arguments.rb".freeze, "lib/bake/base.rb".freeze, "lib/bake/command.rb".freeze, "lib/bake/command/call.rb".freeze, "lib/bake/command/list.rb".freeze, "lib/bake/command/top.rb".freeze, "lib/bake/context.rb".freeze, "lib/bake/documentation.rb".freeze, "lib/bake/format.rb".freeze, "lib/bake/format/json.rb".freeze, "lib/bake/format/ndjson.rb".freeze, "lib/bake/format/raw.rb".freeze, "lib/bake/format/yaml.rb".freeze, "lib/bake/recipe.rb".freeze, "lib/bake/registry.rb".freeze, "lib/bake/registry/aggregate.rb".freeze, "lib/bake/registry/bakefile_loader.rb".freeze, "lib/bake/registry/directory_loader.rb".freeze, "lib/bake/scope.rb".freeze, "lib/bake/type.rb".freeze, "lib/bake/type/any.rb".freeze, "lib/bake/type/array.rb".freeze, "lib/bake/type/boolean.rb".freeze, "lib/bake/type/decimal.rb".freeze, "lib/bake/type/float.rb".freeze, "lib/bake/type/hash.rb".freeze, "lib/bake/type/input.rb".freeze, "lib/bake/type/integer.rb".freeze, "lib/bake/type/nil.rb".freeze, "lib/bake/type/output.rb".freeze, "lib/bake/type/string.rb".freeze, "lib/bake/type/symbol.rb".freeze, "lib/bake/type/tuple.rb".freeze, "lib/bake/version.rb".freeze, "license.md".freeze, "readme.md".freeze, "releases.md".freeze] s.homepage = "https://github.com/ioquatix/bake".freeze s.licenses = ["MIT".freeze] s.required_ruby_version = Gem::Requirement.new(">= 3.3".freeze) s.rubygems_version = "4.0.3".freeze s.summary = "A replacement for rake with a simpler syntax.".freeze s.specification_version = 4 s.add_runtime_dependency(%q.freeze, [">= 0".freeze]) s.add_runtime_dependency(%q.freeze, ["~> 2.1".freeze]) end bake-0.25.0/license.md0000644000004100000410000000214415216537407014507 0ustar www-datawww-data# MIT License Copyright, 2020-2025, by Samuel Williams. Copyright, 2020-2023, by Olle Jonsson. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. bake-0.25.0/bake/0000755000004100000410000000000015216537407013444 5ustar www-datawww-databake-0.25.0/bake/output.rb0000644000004100000410000000230715216537407015333 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2025, by Samuel Williams. def initialize(...) super require_relative "../lib/bake/format" end # Dump the last result to the specified file (defaulting to stdout) in the specified format (defaulting to Ruby's pretty print). # @parameter file [Output] The input file. # @parameter format [Symbol] The output format. def output(input:, file: $stdout, format: nil) if format = format_for(file, format) format.output(file, input) else raise "Unable to determine output format!" end # Allow chaining of output processing: return input end # Do not produce any output. def null(input:) # This is a no-op, used to indicate that no output should be produced. return input end # This command produces output, and therefore doesn't need default output handling. def output?(recipe) true end private def format_for(file, name) if file.respond_to?(:path) and path = file.path name ||= file_type(path) end # Default to pretty print: name ||= :raw Bake::Format[name] end def file_type(path) if extension = File.extname(path) extension.sub!(/\A\./, "") return if extension.empty? return extension.to_sym end end bake-0.25.0/bake/input.rb0000644000004100000410000000233515216537407015133 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2025, by Samuel Williams. def initialize(...) super require_relative "../lib/bake/format" end # Parse an input file (defaulting to stdin) in the specified format. The format can be extracted from the file extension if left unspecified. # @parameter file [Input] The input file. # @parameter format [Symbol] The input format, e.g. json, yaml. def input(file: $stdin, format: nil) if format = format_for(file, format) format.input(file) else raise "Unable to determine input format of #{file}!" end end # Parse some input text in the specified format (defaulting to JSON). # @parameter text [String] The input text. # @parameter format [Symbol] The input format, e.g. json, yaml. def parse(text, format: :json) file = StringIO.new(text) if format = format_for(nil, format) format.input(file) else raise "Unable to determine input format!" end end private def format_for(file, name) if file.respond_to?(:path) and path = file.path name ||= file_type(path) end Bake::Format[name] end def file_type(path) if extension = File.extname(path) extension.sub!(/\A\./, "") return if extension.empty? return extension.to_sym end end bake-0.25.0/metadata.gz.sig0000444000004100000410000000060015216537407015437 0ustar www-datawww-data-[GTŢ0ИMaۇmq*d?MqfќhZtFgϛ8|Bdcc4'L*\ `@/BD!xPILWr/\qQ?rP#ҡHMG1JE( xX?&|Z>l=Sc5w.&Э bake input --file data.json --format json output [{"name"=>"Alice", "age"=>30}, {"name"=>"Bob", "age"=>40}] ``` ### Text Instead of using a file, you can parse a string argument. ``` shell > bin/bake input:parse "[1,2,3]" output [1, 2, 3] ``` ## Output The `output` task takes the return value from the previous command and outputs it to `stdout` or the specified `--file` path. It also handles a number of differnt `--format` arguments including `json`, `yaml`, `raw` and the default `pp`. ``` shell > bin/bake input --file data.json output --format yaml --- - name: Alice age: 30 - name: Bob age: 40 ``` bake-0.25.0/context/command-line-interface.md0000644000004100000410000000441115216537407021051 0ustar www-datawww-data# Command Line Interface The `bake` command is broken up into two main functions: `list` and `call`.
% bake --help
bake [-h/--help] [-b/--bakefile <path>] <command>
	Execute tasks using Ruby.

	[-h/--help]             Show help.                               
	[-b/--bakefile <path>]  Override the path to the bakefile to use.
	<command>               One of: call, list.                        (default: call)

	call <commands...>
		Execute one or more commands.

		<commands...>  The commands & arguments to invoke.  (default: ["default"])

	list <pattern>
		<pattern>  The pattern to filter tasks by.
## List The `bake list` command allows you to list all available recipes. By proving a pattern you will only see recipes that have a matching command name.
$ bake list console
Bake::Loader console-1.8.2

	console:info
		Increase the verbosity of the logger to info.

	console:debug
		Increase the verbosity of the logger to debug.
The listing documents positional and optional arguments. The documentation is generated from the comments in the bakefiles. ## Call The `bake call` (the default, so `call` can be omitted) allows you to execute one or more recipes. You must provide the name of the command, followed by any arguments.
$ bake async:http:head https://www.codeotaku.com/index
                HEAD: https://www.codeotaku.com/index
             version: h2
              status: 200
                body: body with length 7879B
        content-type: "text/html; charset=utf-8"
       cache-control: "public, max-age=3600"
             expires: "Mon, 04 May 2020 13:23:47 GMT"
              server: "falcon/0.36.4"
                date: "Mon, 04 May 2020 12:23:47 GMT"
                vary: "accept-encoding"
You can specify multiple commands and they will be executed sequentially. bake-0.25.0/context/gem-integration.md0000644000004100000410000000523415216537407017645 0ustar www-datawww-data# Gem Integration This guide explains how to add `bake` to a Ruby gem and export standardised tasks for use by other gems and projects. ## Exporting Tasks Adding a `bake/` directory to your gem will allow other gems and projects to consume those recipes. In order to prevent collisions, you *should* prefix your commands with the name of the gem, e.g. in `mygem/bake/mygem.rb`: ~~~ ruby def setup # ... end ~~~ Then, in a different project which depends on `mygem`, you can run tasks from `mygem` by invoking them using `bake`: ~~~ bash $ bake mygem:setup ~~~ ## Examples There are many gems which export tasks in this way. Here are some notable examples: ### Variant The [variant gem](https://github.com/socketry/variant) exposes bake tasks for setting the environment e.g. `development`, `testing`, or `production`.
$ bake list variant
Bake::Loader variant-0.1.1

	variant:production **overrides
		Select the production variant.
		overrides [Hash] any specific variant overrides.

	variant:staging **overrides
		Select the staging variant.
		overrides [Hash] any specific variant overrides.

	variant:development **overrides
		Select the development variant.
		overrides [Hash] any specific variant overrides.

	variant:testing **overrides
		Select the testing variant.
		overrides [Hash] any specific variant overrides.

	variant:force name **overrides
		Force a specific variant.
		name [Symbol] the default variant.
		overrides [Hash] any specific variant overrides.

	variant:show
		Show variant-related environment variables.
### Console The [console gem](https://github.com/socketry/console) exposes bake tasks to change the log level.
$ bake list console
Bake::Loader console-1.8.2

	console:info
		Increase the verbosity of the logger to info.

	console:debug
		Increase the verbosity of the logger to debug.
bake-0.25.0/context/project-integration.md0000644000004100000410000000363015216537407020541 0ustar www-datawww-data# Project Integration This guide explains how to add `bake` to a Ruby project. ## Bakefile At the top level of your project, you can create a `bake.rb` file, which contains top level tasks which are private to your project. ~~~ ruby def cake ingredients = call 'supermarket:shop', 'flour,sugar,cocoa' lookup('mixer:add').call(ingredients) end ~~~ This file is project specific and is the only file which can expose top level tasks (i.e. without a defined namespace). When used in a gem, these tasks are not exposed to other gems/projects. ## Recipes Alongside the `bake.rb`, there is a `bake/` directory which contains files like `supermarket.rb`. These files contain recipes, e.g.: ~~~ ruby # @parameter ingredients [Array(Any)] the ingredients to purchase. def shop(ingredients) supermarket = Supermarket.best return supermarket.purchase(ingredients) end ~~~ These methods are automatically scoped according to the file name, e.g. `bake/supermarket.rb` will define `supermarket:shop`. ## Arguments Arguments work as normal. Documented types are used to parse strings from the command line. Both positional and optional parameters are supported. ### Positional Parameters Positional parameters are non-keyword parameters which may have a default value. However, because of the limits of the command line, all positional arguments must be specified. ~~~ ruby # @parameter x [Integer] # @parameter y [Integer] def add(x, y) puts x + y end ~~~ Which is invoked by `bake add 1 2`. ### Optional Parameters Optional parameters are keyword parameters which may have a default value. The parameter is set on the command line using the name of the parameter followed by an equals sign, followed by the value. ~~~ ruby # @parameter x [Integer] # @parameter y [Integer] def add(x:, y: 2) puts x + y end ~~~ Which is invoked by `bake add x=1`. Because `y` is not specified, it will default to `2` as per the method definition. bake-0.25.0/context/getting-started.md0000644000004100000410000000547715216537407017672 0ustar www-datawww-data# Getting Started This guide gives a general overview of `bake` and how to use it. ## Installation Add the gem to your project: ~~~ bash $ bundle add bake ~~~ ## Core Concepts `bake` has several core concepts: - A `bake` executable used for invoking one or more tasks. - A {ruby Bake::Context} instance which is bound to a project or gem and exposes a hierarchy of runnable tasks. - A {ruby Bake::Loader::Aggregate} instance which is used for on-demand loading of bake files from the current project and all available gems. ## Executing Tasks The `bake` executable can be used to execute tasks in a `bake.rb` file in the same directory. ``` ruby # bake.rb def add(x, y) puts Integer(x) + Integer(y) end ``` You can execute this task from the command line: ``` shell % bake add 10 20 30 ``` ### Executing Multiple Tasks The `bake` executable can execute multiple tasks in one invocation and even pass the output of one task into a subsequent task. ``` ruby # bake.rb attr_accessor :value ``` You can set and print the value: ``` shell % bake value= 10 value output ``` This is essentially broken down into three operations: `value = 10`, `value` & `output`. The `value` method returns the current value and the `output` task prints the result of the previous task. ## Optional Arguments You can provide optional arguments: ``` ruby # bake.rb def add(x: 10, y: 20) puts Integer(x) + Integer(y) end ``` You can execute this task from the command line: ``` shell % bake add --x 10 --y 20 30 ``` Or alternatively: ``` shell % bake add x=10 y=20 30 ``` ### Using Types You can annotate your task with a type signature and `bake` will coerce your arguments to these types: ``` ruby # bake.rb # @parameter x [Integer] # @parameter y [Integer] def add(x, y) puts x + y end ``` You can execute this task from the command line: ``` shell % bake add 10 20 30 ``` The values are automatically coerced to `Integer`. ### Extending With Documentation You can add documentation to your tasks and parameters (using Markdown formatting). ``` ruby # bake.rb # Add the x and y coordinate together and print the result. # @parameter x [Integer] The x offset. # @parameter y [Integer] The y offset. def add(x, y) puts x + y end ``` You can see this documentation in the task listing: ``` shell % bake list add Bake::Context getting-started add x y Add the x and y coordinate together and print the result. x [Integer] The x offset. y [Integer] The y offset. ``` ### Private Methods If you want to add helper methods which don't show up as tasks, define them as `protected` or `private`. ``` ruby # bake.rb # Add the x and y coordinate together and print the result. # @parameter x [Integer] The x offset. # @parameter y [Integer] The y offset. def add(x, y) puts x + y end private def puts(*arguments) $stdout.puts arguments.inspect end ```