Skip to content

Commit 950d189

Browse files
committed
Enhance DSL
1 parent bf03da4 commit 950d189

File tree

10 files changed

+236
-192
lines changed

10 files changed

+236
-192
lines changed

README.md

Lines changed: 107 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Sam [![Build Status](https://travis-ci.org/imdrasil/sam.cr.svg)](https://travis-ci.org/imdrasil/sam.cr) [![Latest Release](https://img.shields.io/github/release/imdrasil/sam.cr.svg)](https://github.com/imdrasil/sam.cr/releases)
22

3-
Sam is a Make-like utility which allows to specify tasks like Ruby's Rake do using plain Crystal.
3+
Sam is a Make-like utility which allows to specify tasks like Ruby's Rake do using plain Crystal code. This allows you to reuse existing application code base and/or include tasks from your dependencies.
44

55
## Installation
66

@@ -12,171 +12,177 @@ dependencies:
1212
github: imdrasil/sam.cr
1313
```
1414
15-
## Usage
16-
17-
### Simple example
15+
After executing `shards install` Sam-file will be added to the root of your project (unless you already have one).
1816

19-
Create `sam.cr` file in your app root directory and paste next:
17+
## Usage
2018

21-
```crystal
22-
# here you should load your app configuration if
23-
# it will be needed to perform tasks
24-
Sam.namespace "db" do
25-
namespace "schema" do
26-
desc "Outputs smth: requires 2 named arguments"
27-
task "load" do |t, args|
28-
puts args["f1"]
29-
t.invoke("1")
30-
t.invoke("schema:1")
31-
t.invoke("db:migrate")
32-
t.invoke("db:db:migrate")
33-
t.invoke("db:ping")
34-
t.invoke("din:dong")
35-
puts "------"
36-
t.invoke("2", {"f2" => 1})
37-
end
19+
### Task
3820

39-
task "1" do
40-
puts "1"
41-
end
21+
Tasks are the main unit in `sam.cr`. Task has a name, a list of prerequisites and a list of actions (block of a code).
4222

43-
task "2", ["1", "db:migrate"] do |t, args|
44-
puts args.named["f2"].as(Int32) + 3
45-
end
46-
end
23+
Sam extends the global context with own DSL. To define a task use `task` method which accepts the task name as the 1st argument.
4724

48-
namespace "db" do
49-
task "schema" do
50-
puts "same as namespace"
51-
end
25+
```crystal
26+
task "name" do
27+
end
28+
```
5229

53-
task "migrate" do
54-
puts "migrate"
55-
end
56-
end
30+
If you want to define prerequisites, add the array with their names as the 2nd argument:
5731

58-
task "ping" do
59-
puts "ping"
60-
end
32+
```crystal
33+
task "name", ["prereq1", "prereq2"] do
6134
end
62-
Sam.help
6335
```
6436

65-
To ran any of this task open prompt in root location and paste:
37+
#### Executing a task
38+
39+
Sam does no magic with your `sam.cr` file - it is just a common `.cr` source file which allows you to recompile it with any possible code you want such amount of times you need. Therefore the most obvious way to execute any task is:
6640

6741
```shell
68-
$ crystal sam.cr -- <your_task_path> [options]
42+
$ crystal sam.cr -- name
6943
```
7044

71-
To get list of all available tasks:
45+
> `--` here means that `name` is passed as an argument to executed file not `crystal` utility.
46+
47+
In addition to this you are able to configure your makefile to invoke sam tasks. This allows you to use shorten variant
7248

7349
```shell
74-
$ crystal sam.cr -- help
50+
$ make sam name
7551
```
7652

77-
Each tasks has own "path" which consists of namespace names and task name joined together by ":".
53+
> This solution still requires `--` in some cases - see the following section.
7854

79-
Also tasks can accept space separated arguments from prompt. To pass named argument (which have associated name) use next rules:
80-
81-
- `-name value`
82-
- `-name "value with spaces"`
83-
- `name=value`
84-
- `name="value with spaces"`
85-
86-
Also just array of arguments can be passed - just past everything needed without any flags anywhere:
55+
To automatically preconfigure makefile run
8756

8857
```shell
89-
$ crystal sam.cr -- <your_task_path> first_raw_option "options with spaces"
58+
$ crystal sam.cr -- generate:makefile
9059
```
9160

92-
All arguments from prompt will be realized as `String`.
61+
This will modify existing Makefile or create new one. Be careful - this will silent all nonexisting makefile tasks on invocation.
9362

94-
To invoke a task, e.g. the first task "load" from example above:
63+
To see a list of all available tasks with their descriptions:
9564

9665
```shell
97-
crystal sam.cr -- db:schema:load -f1 asd
66+
$ crystal sam.cr -- help
9867
```
9968

100-
Makefile-like usage is supported. To autogenerate receipt just call
69+
#### Tasks with arguments
70+
71+
To pass arguments to your task just list them after it's name:
10172

10273
```shell
103-
$ crystal sam.cr -- generate:makefile
74+
> crystal sam.cr -- name john rob ned
10475
```
10576

106-
This will modify existing Makefile or creates new one. Be careful - this will silent all nonexisting tasks. For more details take a look on template in code. This will allow to call tasks in the next way:
77+
They are passed to a task as a 2nd block argument.
10778

108-
```shell
109-
$ make sam some:task raw_arg1
79+
```crystal
80+
task "name" do |_, args|
81+
puts args[0].as(String)
82+
end
11083
```
11184

112-
But for named argument you need to add `--`
85+
`args` here is an instance of `Sam::Args` class that contains arguments and named arguments passed to each task. Any argument passed from a console is treated as a `String` but `Int32` and `Float64` values also can be specified during task invocation from inside of another one.
11386

114-
```shell
115-
$ make sam db:schema:load -- -f1 asd
116-
```
87+
> Each task has own collection of arguments; only prerequisites shares with target task same `Args` instance.
11788

118-
By default it will try to use your samfile in the app root. To override it pass proper way as second argument
89+
As was mentioned named argument also can be specified by the following ways:
90+
91+
- `-argument value`
92+
- `-argument "value with spaces"`
93+
- `argument=value`
94+
- `argument="value with spaces"`
95+
96+
One important restriction with named arguments usage and makefile-style task invocation: `--` should be placed to explicitly specify that specified arguments belongs to compiled program not crystal compiler:
11997

12098
```shell
121-
$ crystal src/sam.cr -- generate:makefile "src/sam.cr"
99+
$ make sam name john
100+
$ # but
101+
$ make same name -- argument=john
122102
```
123103

124-
To autoload Sam files from your dependencies - just past
104+
More than one task can be specified (even with own arguments) - just separate them by `@` symbol:
125105

126-
```crystal
127-
load_dependencies "dep1", "dep2"`
106+
```shell
107+
$ crystal sam.cr -- name john @ surname argument=snow
128108
```
129109

130-
If library provides some optional files with tasks they could be loaded as well using named tuple literal:
110+
#### Accessing tasks programmatically
111+
112+
Sam allow you to invoke tasks within another ones and even passing own args object. To do this just call `#invoke` method with task name (and arguments if needed) on task object passed as 1st argument:
131113

132114
```crystal
133-
load_dependencies "lib1", "lib2": "special_file", "lib3": ["special_file"], "lib3": ["/root_special_file"]
115+
task "name" do |t|
116+
t.invoke("surname")
117+
end
118+
119+
task "surname" do
120+
puts "Snow"
121+
end
134122
```
135123

136-
By default any nested dependency will be loaded from "tasks" folder at the lib root level. Any dependency with leading "/" makes to load them using given path. So `root_special_file` for `lib3` will be loaded with `lib3/src/lib3/root_special_file.cr`.
124+
If specified task was already invoked before - it will be ignored. To force task invocation - use `#execute`.
137125

138-
To execute multiple tasks at once just list them separated by `@` character:
139126

140-
```shell
141-
$ crystal sam.cr -- namespace1:task1 arg1=2 @ other_task arg1=3
142-
```
127+
Another task could be invoked from current using `invoke` method. It has next signatures:
128+
129+
### Namespaces
143130

144-
Each task will be executed only if the previous one is successfully finished (without throwing any exception).
131+
as projects grow amount of defined tasks grow as well. To simplify navigation and increase readability tasks can be grouped in namespaces:
145132

146-
#### Namespace
133+
```crystal
134+
namespace "main" do
135+
task "build" do
136+
# Build the main program
137+
end
138+
end
147139
148-
To define namespace (for now they only used for namespacing tasks) use `Sam.namespace` (opens `root` namespace) or just `namespace` inside of it. `Sam.namespace` can be called any times - everything will be added to existing staff.
140+
namespace "samples" do
141+
task "build" do
142+
# Build the sample programs
143+
end
144+
end
149145
150-
#### Task
146+
task "build", %w[main:build samples:build] do
147+
end
148+
```
151149

152-
To define task use `task` method with it's name and block. Given block could take 0..2 arguments: `Task` object and `Args` object. Also as second parameter could be provided array of dependent tasks which will be invoked before current.
150+
#### Name resolution
153151

154-
Another task could be invoked from current using `invoke` method. It has next signatures:
152+
When task is invoked from other one, provided path will float up through current task namespace and search given task path on each level until top level. Task could have same name as any existing namespace.
155153

156-
-
157-
- `name : String` - task path
154+
```crystal
155+
task "one" do
156+
end
158157
159-
-
160-
- `name : String` - task path
161-
- `args : Args` - prepared argument object
158+
namespace "one" do
159+
namespace "two"
160+
task "test" do |t|
161+
t.invoke("one")
162+
end
163+
end
164+
end
165+
```
162166

163-
-
164-
- `name : String` - task path
165-
- `hash : Hash(String, String | Int32, Float32)` - hash with arguments
167+
In the example above next paths are checked (in given order):
166168

167-
-
168-
- `name : String` - task path
169-
- `args : Tuple` - raw arguments
169+
* `one:two:one`
170+
* `one:one`
171+
* `one` (as task not namespace)
170172

171-
Any already invoked task is ignored during further invocations. To avoid this `#execute` method could be used.
173+
### Share tasks
172174

173-
#### Routing
175+
Sam tasks can be loaded from installed dependencies. To do this helper macro `load_dependencies` can be used:
174176

175-
When task is invoked from other one provided path will float up through current task namespace nesting and search given path on each level. Task could have same name as any existing namespace.
177+
```crystal
178+
load_dependencies "lib1", "lib2"
179+
```
176180

177-
#### Args
181+
This is translated to
178182

179-
This class represents argument set for task. It can handle named arguments and just raw array of arguments. Now it supports only `String`, `Int32` and `Float64` types. To get access to named argument you can use `[](name : String)` and `[]?(name : String)` methods. For raw attributes there are `[](index : Int32)` and `[]?(index : Int32)` as well.
183+
```crystal
184+
require "./lib/lib1/tasks/sam.cr"
185+
```
180186

181187
## Development
182188

examples/sam.template

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
require "sam"
2+
3+
# Here you can define your tasks
4+
# desc "with description to be used by help command"
5+
# task "test" do
6+
# puts "ping"
7+
# end
8+
9+
Sam.help

shard.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
name: sam
2-
version: 0.3.0
2+
version: 0.3.1
33

44
authors:
55
- Roman Kalnytskyi <moranibaca@gmail.com>
66

7-
crystal: 0.26.0
7+
crystal: 0.30.1
88

99
license: MIT
10+
11+
scripts:
12+
postinstall: "false | cp -i examples/sam.template ../../sam.cr 2>/dev/null"

spec/spec_helper.cr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ end
2828

2929
# Tasks
3030

31-
Sam.namespace "db" do
31+
namespace "db" do
3232
namespace "schema" do
3333
task "load" do |t, args|
3434
puts args["f1"]

0 commit comments

Comments
 (0)