bake 0.12.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5451e66cf2062b167b8b9983bb0308612b6234de8f99af360ddb1098e74d973e
4
- data.tar.gz: feb9f86818d18175eab079b7875c3b3810d1017dcb82f717aaab2f02d04d44ed
3
+ metadata.gz: e4138c750ccdded2e0b36d082d94fecc0ae4afd0cfda6bad6e47179e37450f47
4
+ data.tar.gz: 236deaa42f0a148e195abdbeca1a8298e252ee335ea5cbceddc97e5c06dce195
5
5
  SHA512:
6
- metadata.gz: 6043f4715eafdb780fea3c09ad50fe262d3e2a80234c4c76884b887bf0460e96f652bca576f5f5215d1d0aada420f33a0c8894fbc45ef2b10e716a075890e97c
7
- data.tar.gz: dd58f182d931c1844d5b50794e55d0dd4ec5933fa201e2206adb6abf879b2aff19ab6030c0b2035186e748d5ef1070c34c518573d5fc3c4b7c8f679687ffe5af
6
+ metadata.gz: 0b249b795b9f91489d0150a5d511d3d22a6d4d2292fba3f2680455a2831c9b3480abed70d107127bf45cf76d5197395b9c9be597902800c39ca95be6e761ade8
7
+ data.tar.gz: ec9e06a913c3f457b227d077ae7d2e0f19d90ac7a417edcb3f1d721c0d6176dd62a178d5b6e2db5ff66084e60fda446af5c9f0ee013da98435fc15287e2f0726
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  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.
4
4
 
5
- [![Actions Status](https://github.com/ioquatix/bake/workflows/Development/badge.svg)](https://github.com/ioquatix/bake/actions?workflow=Development)
5
+ [![Development](https://github.com/ioquatix/bake/workflows/Development/badge.svg)](https://github.com/ioquatix/bake/actions?workflow=Development)
6
6
 
7
7
  ## Motivation
8
8
 
@@ -23,81 +23,7 @@ Execute the following in your project:
23
23
 
24
24
  ## Usage
25
25
 
26
- Bake follows similar patterns to Rake.
27
-
28
- ![Example](example.png)
29
-
30
- ## Bakefile
31
-
32
- There is a root level `bake.rb` e.g.:
33
-
34
- ```ruby
35
- def cake
36
- ingredients = call 'supermarket:shop', 'flour,sugar,cocoa'
37
- lookup('mixer:add').call(ingredients)
38
- end
39
- ```
40
-
41
- 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.
42
-
43
- ## Recipes
44
-
45
- Alongside the `bake.rb`, there is a `bake/` directory which contains files like `supermarket.rb`. These files contain recipes, e.g.:
46
-
47
- ```ruby
48
- # @param ingredients [Array(Any)] the ingredients to purchase.
49
- def shop(ingredients)
50
- supermarket = Supermarket.best
51
-
52
- return supermarket.purchase(ingredients)
53
- end
54
- ```
55
-
56
- These methods are automatically scoped according to the file name, e.g. `bake/supermarket.rb` will define `supermarket:shop`.
57
-
58
- ## Gems
59
-
60
- 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`:
61
-
62
- ```ruby
63
- def setup
64
- # ...
65
- end
66
- ```
67
-
68
- Then, in your project `myproject` which depends on `mygem`:
69
-
70
- ```
71
- bake mygem:setup
72
- ```
73
-
74
- ## Arguments
75
-
76
- Arguments work as normal. Documented types are used to parse strings from the command line. Both positional and optional parameters are supported.
77
-
78
- ### Positional Parameters
79
-
80
- ```ruby
81
- # @param x [Integer]
82
- # @param y [Integer]
83
- def add(x, y)
84
- puts x + y
85
- end
86
- ```
87
-
88
- Which is invoked by `bake add 1 2`.
89
-
90
- ### Optional Parameters
91
-
92
- ```ruby
93
- # @param x [Integer]
94
- # @param y [Integer]
95
- def add(x:, y:)
96
- puts x + y
97
- end
98
- ```
99
-
100
- Which is invoked by `bake add x=1 y=2`.
26
+ Please see the <a href="https://ioquatix.github.io/bake/">project documentation</a> or run it locally using `bake utopia:project:serve`.
101
27
 
102
28
  ## Contributing
103
29
 
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
14
14
  spec.metadata["donation_uri"] = "https://github.com/sponsors/ioquatix"
15
15
 
16
16
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
17
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(docs|test|spec|features)/}) }
18
18
  end
19
19
 
20
20
  spec.bindir = "bin"
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
 
26
26
  spec.add_development_dependency 'bake-bundler'
27
27
 
28
+ spec.add_development_dependency 'utopia-project'
28
29
  spec.add_development_dependency 'covered'
29
30
  spec.add_development_dependency 'bundler'
30
31
  spec.add_development_dependency 'rspec'
data/gems.rb CHANGED
@@ -2,4 +2,3 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in bake.gemspec
4
4
  gemspec
5
-
@@ -0,0 +1,55 @@
1
+ # Command Line Interface
2
+
3
+ The `bake` command is broken up into two main functions: `list` and `call`.
4
+
5
+ <pre>% bake --help
6
+ <b>bake [-h/--help] [-b/--bakefile &lt;path&gt;] &lt;command&gt;</b>
7
+ <font color="#638FFF">Execute tasks using Ruby.</font>
8
+
9
+ [-h/--help] Show help.
10
+ [-b/--bakefile &lt;path&gt;] Override the path to the bakefile to use.
11
+ &lt;command&gt; One of: call, list. (default: call)
12
+
13
+ <b>call &lt;commands...&gt;</b>
14
+ <font color="#638FFF">Execute one or more commands.</font>
15
+
16
+ &lt;commands...&gt; The commands &amp; arguments to invoke. (default: [&quot;default&quot;])
17
+
18
+ <b>list &lt;pattern&gt;</b>
19
+ &lt;pattern&gt; The pattern to filter tasks by.
20
+ </pre>
21
+
22
+ ## List
23
+
24
+ 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.
25
+
26
+ <pre>$ bake list console
27
+ <b>Bake::Loader console-1.8.2</b>
28
+
29
+ <b>console:info</b>
30
+ <font color="#638FFF">Increase the verbosity of the logger to info.</font>
31
+
32
+ <b>console:debug</b>
33
+ <font color="#638FFF">Increase the verbosity of the logger to debug.</font>
34
+ </pre>
35
+
36
+ The listing documents positional and optional arguments. The documentation is generated from the comments in the bakefiles.
37
+
38
+ ## Call
39
+
40
+ 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.
41
+
42
+ <pre>$ bake async:http:head https://www.codeotaku.com/index
43
+ <font color="#638FFF"><b> HEAD</b></font>: https://www.codeotaku.com/index
44
+ <font color="#00AA00"><b> version</b></font>: h2
45
+ <font color="#00AA00"><b> status</b></font>: 200
46
+ <font color="#00AA00"><b> body</b></font>: body with length <b>7879B</b>
47
+ <b> content-type</b>: &quot;text/html; charset=utf-8&quot;
48
+ <b> cache-control</b>: &quot;public, max-age=3600&quot;
49
+ <b> expires</b>: &quot;Mon, 04 May 2020 13:23:47 GMT&quot;
50
+ <b> server</b>: &quot;falcon/0.36.4&quot;
51
+ <b> date</b>: &quot;Mon, 04 May 2020 12:23:47 GMT&quot;
52
+ <b> vary</b>: &quot;accept-encoding&quot;
53
+ </pre>
54
+
55
+ You can specify multiple commands and they will be executed sequentially.
@@ -0,0 +1,69 @@
1
+ # Gem Integration
2
+
3
+ This guide explains how to add `bake` to a Ruby gem and export standardised tasks for use by other gems and projects.
4
+
5
+ ## Exporting Tasks
6
+
7
+ 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`:
8
+
9
+ ~~~ ruby
10
+ def setup
11
+ # ...
12
+ end
13
+ ~~~
14
+
15
+ Then, in a different project which depends on `mygem`, you can run tasks from `mygem` by invoking them using `bake`:
16
+
17
+ ~~~ bash
18
+ $ bake mygem:setup
19
+ ~~~
20
+
21
+ ## Examples
22
+
23
+ There are many gems which export tasks in this way. Here are some notable examples:
24
+
25
+ ### Variant
26
+
27
+ The [variant gem](https://github.com/socketry/variant) exposes bake tasks for setting the environment e.g. `development`, `testing`, or `production`.
28
+
29
+ <pre class="terminal">$ bake list variant
30
+ <b>Bake::Loader variant-0.1.1</b>
31
+
32
+ <b>variant:production</b> <font color="#00AA00">**overrides</font>
33
+ <font color="#638FFF">Select the production variant.</font>
34
+ <font color="#00AA00">overrides</font> [Hash] <font color="#638FFF">any specific variant overrides.</font>
35
+
36
+ <b>variant:staging</b> <font color="#00AA00">**overrides</font>
37
+ <font color="#638FFF">Select the staging variant.</font>
38
+ <font color="#00AA00">overrides</font> [Hash] <font color="#638FFF">any specific variant overrides.</font>
39
+
40
+ <b>variant:development</b> <font color="#00AA00">**overrides</font>
41
+ <font color="#638FFF">Select the development variant.</font>
42
+ <font color="#00AA00">overrides</font> [Hash] <font color="#638FFF">any specific variant overrides.</font>
43
+
44
+ <b>variant:testing</b> <font color="#00AA00">**overrides</font>
45
+ <font color="#638FFF">Select the testing variant.</font>
46
+ <font color="#00AA00">overrides</font> [Hash] <font color="#638FFF">any specific variant overrides.</font>
47
+
48
+ <b>variant:force</b> <font color="#AA0000">name</font> <font color="#00AA00">**overrides</font>
49
+ <font color="#638FFF">Force a specific variant.</font>
50
+ <font color="#00AA00">name</font> [Symbol] <font color="#638FFF">the default variant.</font>
51
+ <font color="#00AA00">overrides</font> [Hash] <font color="#638FFF">any specific variant overrides.</font>
52
+
53
+ <b>variant:show</b>
54
+ <font color="#638FFF">Show variant-related environment variables.</font>
55
+ </pre>
56
+
57
+ ### Console
58
+
59
+ The [console gem](https://github.com/socketry/console) exposes bake tasks to change the log level.
60
+
61
+ <pre class="terminal">$ bake list console
62
+ <b>Bake::Loader console-1.8.2</b>
63
+
64
+ <b>console:info</b>
65
+ <font color="#638FFF">Increase the verbosity of the logger to info.</font>
66
+
67
+ <b>console:debug</b>
68
+ <font color="#638FFF">Increase the verbosity of the logger to debug.</font>
69
+ </pre>
@@ -0,0 +1,64 @@
1
+ # Project Integration
2
+
3
+ This guide explains how to add `bake` to a Ruby project.
4
+
5
+ ## Bakefile
6
+
7
+ 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.
8
+
9
+ ~~~ ruby
10
+ def cake
11
+ ingredients = call 'supermarket:shop', 'flour,sugar,cocoa'
12
+ lookup('mixer:add').call(ingredients)
13
+ end
14
+ ~~~
15
+
16
+ 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.
17
+
18
+ ## Recipes
19
+
20
+ Alongside the `bake.rb`, there is a `bake/` directory which contains files like `supermarket.rb`. These files contain recipes, e.g.:
21
+
22
+ ~~~ ruby
23
+ # @param ingredients [Array(Any)] the ingredients to purchase.
24
+ def shop(ingredients)
25
+ supermarket = Supermarket.best
26
+
27
+ return supermarket.purchase(ingredients)
28
+ end
29
+ ~~~
30
+
31
+ These methods are automatically scoped according to the file name, e.g. `bake/supermarket.rb` will define `supermarket:shop`.
32
+
33
+
34
+ ## Arguments
35
+
36
+ Arguments work as normal. Documented types are used to parse strings from the command line. Both positional and optional parameters are supported.
37
+
38
+ ### Positional Parameters
39
+
40
+ 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.
41
+
42
+ ~~~ ruby
43
+ # @param x [Integer]
44
+ # @param y [Integer]
45
+ def add(x, y)
46
+ puts x + y
47
+ end
48
+ ~~~
49
+
50
+ Which is invoked by `bake add 1 2`.
51
+
52
+ ### Optional Parameters
53
+
54
+ 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.
55
+
56
+ ~~~ ruby
57
+ # @param x [Integer]
58
+ # @param y [Integer]
59
+ def add(x:, y: 2)
60
+ puts x + y
61
+ end
62
+ ~~~
63
+
64
+ Which is invoked by `bake add x=1`. Because `y` is not specified, it will default to `2` as per the method definition.
@@ -22,7 +22,10 @@ require_relative 'recipe'
22
22
  require_relative 'scope'
23
23
 
24
24
  module Bake
25
+ # The base class for including {Scope} instances which define {Recipe} instances.
25
26
  class Base < Struct.new(:context)
27
+ # Generate a base class for the specified path.
28
+ # @param path [Array(String)] The command path.
26
29
  def self.derive(path = [])
27
30
  klass = Class.new(self)
28
31
 
@@ -31,6 +34,8 @@ module Bake
31
34
  return klass
32
35
  end
33
36
 
37
+ # Format the class as a command.
38
+ # @return [String]
34
39
  def self.to_s
35
40
  if path = self.path
36
41
  path.join(':')
@@ -47,20 +52,31 @@ module Bake
47
52
  end
48
53
  end
49
54
 
55
+ # The path of this derived base class.
56
+ # @return [Array(String)]
50
57
  def self.path
51
58
  self.const_get(:PATH)
52
59
  rescue
53
60
  nil
54
61
  end
55
62
 
63
+ # The path for this derived base class.
64
+ # @return [Array(String)]
56
65
  def path
57
66
  self.class.path
58
67
  end
59
68
 
69
+ # Proxy a method call using command line arguments through to the {Context} instance.
70
+ # @param arguments [Array(String)]
60
71
  def call(*arguments)
61
72
  self.context.call(*arguments)
62
73
  end
63
74
 
75
+ # Recipes defined in this scope.
76
+ #
77
+ # @block `{|recipe| ...}`
78
+ # @yield recipe [Recipe]
79
+ # @return [Enumerable]
64
80
  def recipes
65
81
  return to_enum(:recipes) unless block_given?
66
82
 
@@ -71,6 +87,9 @@ module Bake
71
87
  end
72
88
  end
73
89
 
90
+ # Look up a recipe with a specific name.
91
+ #
92
+ # @param name [String] The instance method to look up.
74
93
  def recipe_for(name)
75
94
  Recipe.new(self, name)
76
95
  end
@@ -21,6 +21,7 @@
21
21
  require_relative 'command/top'
22
22
 
23
23
  module Bake
24
+ # The command line interface.
24
25
  module Command
25
26
  def self.call(*arguments)
26
27
  Top.call(*arguments)
@@ -26,6 +26,7 @@ require_relative '../context'
26
26
 
27
27
  module Bake
28
28
  module Command
29
+ # Execute one or more commands.
29
30
  class Call < Samovar::Command
30
31
  self.description = "Execute one or more commands."
31
32
 
@@ -23,7 +23,10 @@ require 'set'
23
23
 
24
24
  module Bake
25
25
  module Command
26
+ # List all available commands.
26
27
  class List < Samovar::Command
28
+ self.description = "List all available commands."
29
+
27
30
  one :pattern, "The pattern to filter tasks by."
28
31
 
29
32
  def format_parameters(parameters, terminal)
@@ -26,6 +26,7 @@ require_relative 'list'
26
26
 
27
27
  module Bake
28
28
  module Command
29
+ # The top level command line application.
29
30
  class Top < Samovar::Command
30
31
  self.description = "Execute tasks using Ruby."
31
32
 
@@ -56,12 +57,12 @@ module Bake
56
57
  return terminal
57
58
  end
58
59
 
59
- def bakefile
60
+ def bakefile_path
60
61
  @options[:bakefile] || Dir.pwd
61
62
  end
62
63
 
63
64
  def context
64
- Context.load(self.bakefile)
65
+ Context.load(self.bakefile_path)
65
66
  end
66
67
 
67
68
  def call
@@ -21,11 +21,14 @@
21
21
  require_relative 'base'
22
22
 
23
23
  module Bake
24
+ # The default file name for the top level bakefile.
24
25
  BAKEFILE = "bake.rb"
25
26
 
27
+ # Represents a context of task execution, containing all relevant state.
26
28
  class Context
29
+ # Search upwards from the specified path for a {BAKEFILE}.
27
30
  # 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.
28
- # @return [String, nil] the path to the bakefile if it could be found.
31
+ # @return [String | Nil] The path to the bakefile if it could be found.
29
32
  def self.bakefile_path(path, bakefile: BAKEFILE)
30
33
  if File.file?(path)
31
34
  return path
@@ -52,6 +55,8 @@ module Bake
52
55
  return nil
53
56
  end
54
57
 
58
+ # Load a context from the specified path.
59
+ # @path [String] A file-system path.
55
60
  def self.load(path = Dir.pwd)
56
61
  if bakefile_path = self.bakefile_path(path)
57
62
  scope = Scope.load(bakefile_path)
@@ -68,6 +73,8 @@ module Bake
68
73
  return self.new(loaders, scope, working_directory)
69
74
  end
70
75
 
76
+ # Initialize the context with the specified loaders.
77
+ # @param loaders [Loaders]
71
78
  def initialize(loaders, scope = nil, root = nil)
72
79
  @loaders = loaders
73
80
 
@@ -92,10 +99,18 @@ module Bake
92
99
  end
93
100
  end
94
101
 
102
+ # The loaders which will be used to resolve recipes in this context.
103
+ attr :loaders
104
+
105
+ # The scope for the root {BAKEFILE}.
95
106
  attr :scope
107
+
108
+ # The root path of this context.
109
+ # @return [String | Nil]
96
110
  attr :root
97
- attr :loaders
98
111
 
112
+ # Invoke recipes on the context using command line arguments.
113
+ # @param commands [Array(String)]
99
114
  def call(*commands)
100
115
  last_result = nil
101
116
 
@@ -111,6 +126,8 @@ module Bake
111
126
  return last_result
112
127
  end
113
128
 
129
+ # Lookup a recipe for the given command name.
130
+ # @param command [String] The command name, e.g. `bundler:release`.
114
131
  def lookup(command)
115
132
  @recipes[command]
116
133
  end
@@ -21,14 +21,21 @@
21
21
  require_relative 'scope'
22
22
 
23
23
  module Bake
24
+ # Structured access to a set of comment lines.
24
25
  class Documentation
25
-
26
+ # Initialize the documentation with an array of comments.
27
+ #
28
+ # @param comments [Array(String)] An array of comment lines.
26
29
  def initialize(comments)
27
30
  @comments = comments
28
31
  end
29
32
 
30
33
  DESCRIPTION = /\A\s*([^@\s].*)?\z/
31
34
 
35
+ # The text-only lines of the comment block.
36
+ #
37
+ # @yield [String]
38
+ # @return [Enumerable]
32
39
  def description
33
40
  return to_enum(:description) unless block_given?
34
41
 
@@ -55,6 +62,11 @@ module Bake
55
62
 
56
63
  ATTRIBUTE = /\A\s*@(?<name>.*?)\s+(?<value>.*?)\z/
57
64
 
65
+ # The attribute lines of the comment block.
66
+ # e.g. `@return [String]`.
67
+ #
68
+ # @yield [String]
69
+ # @return [Enumerable]
58
70
  def attributes
59
71
  return to_enum(:attributes) unless block_given?
60
72
 
@@ -67,6 +79,11 @@ module Bake
67
79
 
68
80
  PARAMETER = /\A\s*@param\s+(?<name>.*?)\s+\[(?<type>.*?)\]\s+(?<details>.*?)\z/
69
81
 
82
+ # The parameter lines of the comment block.
83
+ # e.g. `@param value [String] The value.`
84
+ #
85
+ # @yield [String]
86
+ # @return [Enumerable]
70
87
  def parameters
71
88
  return to_enum(:parameters) unless block_given?
72
89
 
@@ -21,7 +21,10 @@
21
21
  require_relative 'scope'
22
22
 
23
23
  module Bake
24
+ # Represents a directory which contains bakefiles.
24
25
  class Loader
26
+ # Initialize the loader with the specified root path.
27
+ # @param root [String] A file-system path.
25
28
  def initialize(root, name: nil)
26
29
  @root = root
27
30
  @name = name
@@ -31,8 +34,12 @@ module Bake
31
34
  "#{self.class} #{@name || @root}"
32
35
  end
33
36
 
37
+ # The root path for this loader.
34
38
  attr :root
35
39
 
40
+ # Enumerate all bakefiles within the loaders root directory.
41
+ # @block `{|path| ...}`
42
+ # @yield path [String] The Ruby source file path.
36
43
  def each
37
44
  return to_enum unless block_given?
38
45
 
@@ -41,18 +48,8 @@ module Bake
41
48
  end
42
49
  end
43
50
 
44
- def recipe_for(path)
45
- if book = lookup(path)
46
- return book.lookup(path.last)
47
- else
48
- *path, name = *path
49
-
50
- if book = lookup(path)
51
- return book.lookup(name)
52
- end
53
- end
54
- end
55
-
51
+ # Load the {Scope} for the specified relative path within this loader, if it exists.
52
+ # @param path [Array(String)] A relative path.
56
53
  def scope_for(path)
57
54
  *directory, file = *path
58
55
 
@@ -23,9 +23,12 @@ require 'console'
23
23
  require_relative 'loader'
24
24
 
25
25
  module Bake
26
+ # Structured access to the working directory and loaded gems for loading bakefiles.
26
27
  class Loaders
27
28
  include Enumerable
28
29
 
30
+ # Create a loader using the specified working directory.
31
+ # @param working_directory [String]
29
32
  def self.default(working_directory)
30
33
  loaders = self.new
31
34
 
@@ -34,15 +37,20 @@ module Bake
34
37
  return loaders
35
38
  end
36
39
 
40
+ # Initialize an empty array of loaders.
37
41
  def initialize
38
42
  @roots = {}
39
43
  @ordered = Array.new
40
44
  end
41
45
 
46
+ # Whether any loaders are defined.
47
+ # @return [Boolean]
42
48
  def empty?
43
49
  @ordered.empty?
44
50
  end
45
51
 
52
+ # Add loaders according to the current working directory and loaded gems.
53
+ # @param working_directory [String]
46
54
  def append_defaults(working_directory)
47
55
  # Load recipes from working directory:
48
56
  self.append_path(working_directory)
@@ -51,32 +59,26 @@ module Bake
51
59
  self.append_from_gems
52
60
  end
53
61
 
62
+ # Enumerate the loaders in order.
54
63
  def each(&block)
55
- return to_enum unless block_given?
56
-
57
64
  @ordered.each(&block)
58
65
  end
59
66
 
67
+ # Append a specific project path to the search path for recipes.
68
+ # The computed path will have `bake` appended to it.
69
+ # @param current [String] The path to add.
60
70
  def append_path(current = Dir.pwd, **options)
61
- recipes_path = File.join(current, "recipes")
62
71
  bake_path = File.join(current, "bake")
63
72
 
64
73
  if File.directory?(bake_path)
65
74
  return insert(bake_path, **options)
66
75
  end
67
76
 
68
- if File.directory?(recipes_path)
69
- Console.logger.warn(self) do |buffer|
70
- buffer.puts "Recipes path: #{recipes_path.inspect} is deprecated."
71
- buffer.puts "Please use #{bake_path.inspect} instead."
72
- end
73
-
74
- return insert(recipes_path, **options)
75
- end
76
-
77
77
  return false
78
78
  end
79
79
 
80
+ # Search from the current working directory until a suitable bakefile is found and add it.
81
+ # @param current [String] The path to start searching from.
80
82
  def append_from_root(current = Dir.pwd, **options)
81
83
  while current
82
84
  append_path(current, **options)
@@ -91,6 +93,7 @@ module Bake
91
93
  end
92
94
  end
93
95
 
96
+ # Enumerate all loaded gems and add them.
94
97
  def append_from_gems
95
98
  Gem.loaded_specs.each do |name, spec|
96
99
  Console.logger.debug(self) {"Checking gem #{name}: #{spec.full_gem_path}..."}
@@ -101,12 +104,6 @@ module Bake
101
104
  end
102
105
  end
103
106
 
104
- def append_from_paths(paths, **options)
105
- paths.each do |path|
106
- append_path(path, **options)
107
- end
108
- end
109
-
110
107
  protected
111
108
 
112
109
  def insert(directory, **options)
@@ -22,7 +22,13 @@ require_relative 'types'
22
22
  require_relative 'documentation'
23
23
 
24
24
  module Bake
25
+ # Structured access to an instance method in a bakefile.
25
26
  class Recipe
27
+ # Initialize the recipe.
28
+ #
29
+ # @param instance [Base] The instance this recipe is attached to.
30
+ # @param name [String] The method name.
31
+ # @param method [Method | Nil] The method if already known.
26
32
  def initialize(instance, name, method = nil)
27
33
  @instance = instance
28
34
  @name = name
@@ -35,21 +41,29 @@ module Bake
35
41
  @arity = nil
36
42
  end
37
43
 
44
+ # The {Base} instance that this recipe is attached to.
38
45
  attr :instance
46
+
47
+ # The name of this recipe.
39
48
  attr :name
40
49
 
50
+ # Sort by location in source file.
41
51
  def <=> other
42
52
  self.source_location <=> other.source_location
43
53
  end
44
54
 
55
+ # The method implementation.
45
56
  def method
46
57
  @method ||= @instance.method(@name)
47
58
  end
48
59
 
60
+ # The source location of this recipe.
49
61
  def source_location
50
62
  self.method.source_location
51
63
  end
52
64
 
65
+ # The recipe's formal parameters, if any.
66
+ # @return [Array | Nil]
53
67
  def parameters
54
68
  parameters = method.parameters
55
69
 
@@ -58,6 +72,8 @@ module Bake
58
72
  end
59
73
  end
60
74
 
75
+ # Whether this recipe has optional arguments.
76
+ # @return [Boolean]
61
77
  def options?
62
78
  if parameters = self.parameters
63
79
  type, name = parameters.last
@@ -66,6 +82,7 @@ module Bake
66
82
  end
67
83
  end
68
84
 
85
+ # The command name for this recipe.
69
86
  def command
70
87
  @command ||= compute_command
71
88
  end
@@ -74,6 +91,7 @@ module Bake
74
91
  self.command
75
92
  end
76
93
 
94
+ # The method's arity, the required number of positional arguments.
77
95
  def arity
78
96
  if @arity.nil?
79
97
  @arity = method.parameters.count{|type, name| type == :req}
@@ -82,6 +100,10 @@ module Bake
82
100
  return @arity
83
101
  end
84
102
 
103
+ # Process command line arguments into the ordered and optional arguments.
104
+ # @param arguments [Array(String)] The command line arguments
105
+ # @return ordered [Array]
106
+ # @return options [Hash]
85
107
  def prepare(arguments)
86
108
  offset = 0
87
109
  ordered = []
@@ -119,6 +141,7 @@ module Bake
119
141
  return ordered, options
120
142
  end
121
143
 
144
+ # Call the recipe with the specified arguments and options.
122
145
  def call(*arguments, **options)
123
146
  if options?
124
147
  @instance.send(@name, *arguments, **options)
@@ -128,22 +151,20 @@ module Bake
128
151
  end
129
152
  end
130
153
 
131
- def explain(context, *arguments, **options)
132
- if options?
133
- puts "#{self}(#{arguments.join(", ")}, #{options.inspect})"
134
- else
135
- puts "#{self}(#{arguments.join(", ")})"
136
- end
137
- end
138
-
154
+ # Any comments associated with the source code which defined the method.
155
+ # @return [Array(String)] The comment lines.
139
156
  def comments
140
157
  @comments ||= read_comments
141
158
  end
142
159
 
160
+ # The documentation object which provides structured access to the {comments}.
161
+ # @return [Documentation]
143
162
  def documentation
144
163
  @documentation ||= Documentation.new(self.comments)
145
164
  end
146
165
 
166
+ # The documented type signature of the recipe.
167
+ # @return [Array] An array of {Types} instances.
147
168
  def types
148
169
  @types ||= read_types
149
170
  end
@@ -21,7 +21,9 @@
21
21
  require_relative 'recipe'
22
22
 
23
23
  module Bake
24
+ # Used for containing all methods defined in a bakefile.
24
25
  module Scope
26
+ # Load the specified file into a unique scope module, which can then be included into a {Base} instance.
25
27
  def self.load(file_path, path = [])
26
28
  scope = Module.new
27
29
  scope.extend(self)
@@ -38,10 +40,11 @@ module Bake
38
40
  "Bake::Scope<#{self.const_get(:FILE_PATH)}>"
39
41
  end
40
42
 
41
- def recipe(name, **options, &block)
42
- define_method(name, &block)
43
- end
44
-
43
+ # Recipes defined in this scope.
44
+ #
45
+ # @block `{|recipe| ...}`
46
+ # @yield recipe [Recipe]
47
+ # @return [Enumerable]
45
48
  def recipes
46
49
  return to_enum(:recipes) unless block_given?
47
50
 
@@ -52,10 +55,14 @@ module Bake
52
55
  end
53
56
  end
54
57
 
58
+ # The path of the file that was used to {load} this scope.
55
59
  def path
56
60
  self.const_get(:PATH)
57
61
  end
58
62
 
63
+ # Look up a recipe with a specific name.
64
+ #
65
+ # @param name [String] The instance method to look up.
59
66
  def recipe_for(name)
60
67
  Recipe.new(self, name, self.instance_method(name))
61
68
  end
@@ -20,43 +20,70 @@
20
20
 
21
21
  module Bake
22
22
  module Types
23
- module Type
24
- def | other
25
- Any.new([self, other])
26
- end
27
- end
28
-
23
+ # An ordered list of types. The first type to match the input is used.
24
+ #
25
+ # ```ruby
26
+ # type = Bake::Types::Any(Bake::Types::String, Bake::Types::Integer)
27
+ # ```
28
+ #
29
29
  class Any
30
+ # Initialize the instance with an array of types.
31
+ # @param types [Array] the array of types.
30
32
  def initialize(types)
31
33
  @types = types
32
34
  end
33
35
 
36
+ # Create a copy of the current instance with the other type appended.
37
+ # @param other [Type] the type instance to append.
34
38
  def | other
35
39
  self.class.new([*@types, other])
36
40
  end
37
41
 
42
+ # Whether any of the listed types is a composite type.
43
+ # @return [Boolean] true if any of the listed types is `composite?`.
38
44
  def composite?
39
45
  @types.any?{|type| type.composite?}
40
46
  end
41
47
 
48
+ # Parse an input string, trying the listed types in order, returning the first one which doesn't raise an exception.
49
+ # @param input [String] the input to parse, e.g. `"5"`.
42
50
  def parse(input)
43
51
  @types.each do |type|
44
52
  return type.parse(input)
45
53
  rescue
46
- # Ignore
54
+ # Ignore.
47
55
  end
48
56
  end
49
57
 
58
+ # As a class type, accepts any value.
50
59
  def self.parse(value)
51
60
  value
52
61
  end
53
62
 
63
+ # Generate a readable string representation of the listed types.
54
64
  def to_s
55
65
  "any of #{@types.join(', ')}"
56
66
  end
57
67
  end
58
68
 
59
- def self.Any(types)
69
+ # An extension module which allows constructing `Any` types using the `|` operator.
70
+ module Type
71
+ # Create an instance of `Any` with the arguments as types.
72
+ # @param other [Type] the alternative type to match.
73
+ def | other
74
+ Any.new([self, other])
75
+ end
76
+ end
77
+
78
+ # A type constructor.
79
+ #
80
+ # ```ruby
81
+ # Any(Integer, String)
82
+ # ```
83
+ #
84
+ # See [Any.initialize](#Bake::Types::Any::initialize).
85
+ #
86
+ def self.Any(*types)
60
87
  Any.new(types)
61
88
  end
62
89
  end
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Bake
22
- VERSION = "0.12.1"
22
+ VERSION = "0.13.0"
23
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bake
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.1
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-30 00:00:00.000000000 Z
11
+ date: 2020-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: samovar
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: utopia-project
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: covered
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -109,8 +123,10 @@ files:
109
123
  - bake.gemspec
110
124
  - bake.rb
111
125
  - bin/bake
112
- - example.png
113
126
  - gems.rb
127
+ - guides/command-line-interface/README.md
128
+ - guides/gem-integration/README.md
129
+ - guides/project-integration/README.md
114
130
  - lib/bake.rb
115
131
  - lib/bake/base.rb
116
132
  - lib/bake/command.rb
Binary file