caml 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README.md +70 -0
- data/bin/caml +64 -0
- data/lib/caml/caml.rb +107 -0
- data/spec/caml/caml_spec.rb +9 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c8112aee1b07f561a5a61568f3d53d7c4bf002de6eb011be5e835490c404cb0f
|
4
|
+
data.tar.gz: a72ec8bf5815f84abb80b585c6ab0d1d84802b23e91672a47844c16cf2894f46
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 113daadc8195e7ea3667d3ac463455e3e9c2f18ff022f3ee030ce68672c14e4ce022b91e16f479b12a6060c2aa626ab8bf16596af29d8e2cd018a0c4fc4ef9c1
|
7
|
+
data.tar.gz: d9d5127a760273c578b486b4754e97d24b278e215f9d9edc3a1ac730ec24fc5a9ae6add0525e183c821ed0c65528d955c136360f58ae51668f2fe49025587a92
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2020 Ian Johnson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# 🐪 caml
|
2
|
+
|
3
|
+
> Build CLI apps with YAML
|
4
|
+
|
5
|
+
`caml` allows you to build command line applications using declarative yaml.
|
6
|
+
`caml` aims to be like `make`, but by using descriptive, declarative yaml.
|
7
|
+
|
8
|
+
## Usage
|
9
|
+
|
10
|
+
Running the command without any arguments displays the commands defined in the `caml.yaml` file:
|
11
|
+
|
12
|
+
```sh
|
13
|
+
bin/caml
|
14
|
+
```
|
15
|
+
|
16
|
+
## Declaring commands
|
17
|
+
|
18
|
+
`caml` reads a file called `caml.yaml` in the current directory and converts those commands into a unified CLI command.
|
19
|
+
|
20
|
+
The basic structure is to have a `command`, which has a `desc` and an `execute` for the bash command to execute.
|
21
|
+
|
22
|
+
```yaml
|
23
|
+
command:
|
24
|
+
desc: Command description
|
25
|
+
execute: script.sh
|
26
|
+
```
|
27
|
+
|
28
|
+
This yaml will create the following command:
|
29
|
+
|
30
|
+
```yaml
|
31
|
+
bin/caml command # Command description
|
32
|
+
```
|
33
|
+
|
34
|
+
And it will run any bash command defined.
|
35
|
+
|
36
|
+
Arguments may be added under `args` in a nested fashion as displayed below.
|
37
|
+
|
38
|
+
```yaml
|
39
|
+
command:
|
40
|
+
args:
|
41
|
+
one:
|
42
|
+
desc: First argument
|
43
|
+
type: string
|
44
|
+
two:
|
45
|
+
desc: Second argument
|
46
|
+
type: string
|
47
|
+
```
|
48
|
+
|
49
|
+
## Examples
|
50
|
+
|
51
|
+
```yaml
|
52
|
+
build:
|
53
|
+
desc: Build our project
|
54
|
+
execute: make
|
55
|
+
clean:
|
56
|
+
desc: Clean our project
|
57
|
+
execute: make clean
|
58
|
+
```
|
59
|
+
|
60
|
+
```yaml
|
61
|
+
build:
|
62
|
+
desc: Bundle
|
63
|
+
execute: bundle install
|
64
|
+
migrate:
|
65
|
+
desc: Migrate the test database
|
66
|
+
execute: rails db:migrate RAILS_ENV=test
|
67
|
+
test:
|
68
|
+
desc: Run tests
|
69
|
+
execute: rspec
|
70
|
+
```
|
data/bin/caml
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
require_relative '../lib/caml/caml'
|
5
|
+
|
6
|
+
# TODO:
|
7
|
+
# x Yaml
|
8
|
+
# x Metaprogramming
|
9
|
+
# _ Thor commands, options, arguments, etc.
|
10
|
+
# _ Add bash and ruby commands
|
11
|
+
# _ Turn into gem
|
12
|
+
# _ Distribute via CMM gem server
|
13
|
+
# _ Make some noise
|
14
|
+
# _ Make it better with Rust
|
15
|
+
# _ Distribute a release on GHE
|
16
|
+
# _ Make some more noise
|
17
|
+
|
18
|
+
|
19
|
+
class CamlCli < Thor
|
20
|
+
def self.load
|
21
|
+
Caml::Config.new.directives.each do |entry|
|
22
|
+
directive = build_directive(entry)
|
23
|
+
args = build_args(directive['args'])
|
24
|
+
opts = build_opts(directive['opts'])
|
25
|
+
command = build_command(directive.merge({'args' => args, 'opts' => opts}))
|
26
|
+
puts command
|
27
|
+
puts ''
|
28
|
+
class_eval command.to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def self.build_directive(entry)
|
35
|
+
entry.last.merge({'name' => entry.first, 'args' => entry.last['args'] || [], 'opts' => entry.last['opts'] || []})
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.build_args(args)
|
39
|
+
args.reject { |arg| arg.empty? }
|
40
|
+
.map { |name, arg| arg.merge({'name' => name}) }
|
41
|
+
.map { |arg| Caml::Argument.new(arg) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.build_opts(opts)
|
45
|
+
opts.reject { |opt| opt.empty? }
|
46
|
+
.map { |name, opt| opt.merge({'name' => name}) }
|
47
|
+
.map { |opt| Caml::Option.new(opt) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.build_command(directive)
|
51
|
+
Caml::Command.new(directive)
|
52
|
+
end
|
53
|
+
|
54
|
+
no_commands do
|
55
|
+
def execute
|
56
|
+
successful = system yield
|
57
|
+
exit successful
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
CamlCli.load
|
64
|
+
CamlCli.start(ARGV)
|
data/lib/caml/caml.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'safe_yaml/load'
|
2
|
+
|
3
|
+
module Caml
|
4
|
+
# def self.wrap(object)
|
5
|
+
# if object.nil?
|
6
|
+
# []
|
7
|
+
# elsif object.respond_to?(:to_ary)
|
8
|
+
# object.to_ary || [object]
|
9
|
+
# else
|
10
|
+
# [object]
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
|
14
|
+
class Config
|
15
|
+
def initialize
|
16
|
+
# Recursively load (prompt to init if it doesn't exist)
|
17
|
+
SafeYAML::OPTIONS[:default_mode] = :safe
|
18
|
+
@config = YAML.load_file(File.join(Dir.getwd, 'caml.yaml'))
|
19
|
+
end
|
20
|
+
|
21
|
+
def directives
|
22
|
+
@config
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
inspect
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Command
|
31
|
+
attr_reader :name, :desc, :aliases, :args, :opts, :execute
|
32
|
+
|
33
|
+
def initialize(options = {})
|
34
|
+
@name = options['name']
|
35
|
+
@desc = options['desc']
|
36
|
+
@aliases = options['aliases']
|
37
|
+
@args = options['args']
|
38
|
+
@opts = options['opts']
|
39
|
+
@execute = options['execute']
|
40
|
+
end
|
41
|
+
|
42
|
+
def dispatcher
|
43
|
+
if opts.empty?
|
44
|
+
executor
|
45
|
+
else
|
46
|
+
first_clause = "if options[:#{opts.first.name.to_sym}]\nexecute { \"#{opts.first.execute}\" }\n"
|
47
|
+
# else_clause = directive.execute.nil? ? "#\n" : "else\nexecute { \"#{wrap(directive.execute).join(';')}\" }\nend"
|
48
|
+
# clauses = directive.options.drop(1).map { |option| "elsif options[:#{option.name.to_sym}]\nexecute { \"#{wrap(option.execute).join(';')}\" }\n" }.join
|
49
|
+
# dispatcher = first_clause + clauses + else_clause
|
50
|
+
puts first_clause
|
51
|
+
executor
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def executor
|
56
|
+
if execute.respond_to?(:join)
|
57
|
+
<<-EOS
|
58
|
+
execute do
|
59
|
+
<<-COMMANDS
|
60
|
+
#{execute.join("\n")}
|
61
|
+
COMMANDS
|
62
|
+
end
|
63
|
+
EOS
|
64
|
+
else
|
65
|
+
"execute { \"#{execute}\" }\n"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_s
|
70
|
+
descriptor = "desc '#{name}', '#{desc}'\n"
|
71
|
+
options = opts.empty? ? '' : opts.join("\n") + "\n"
|
72
|
+
signature = "def #{name}\n" # TODO: args
|
73
|
+
close_scope = "end\n"
|
74
|
+
descriptor + options + signature + executor + close_scope
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Option
|
79
|
+
attr_reader :name, :type, :desc, :aliases, :execute
|
80
|
+
|
81
|
+
def initialize(options = {})
|
82
|
+
@name = options['name']
|
83
|
+
@type = options['type']
|
84
|
+
@desc = options['desc']
|
85
|
+
@aliases = options['aliases']
|
86
|
+
@execute = options['execute']
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_s
|
90
|
+
"option :#{name}, type: :#{type}, desc: '#{desc}'"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class Argument
|
95
|
+
attr_reader :name, :type, :desc, :value
|
96
|
+
|
97
|
+
def initialize(options ={})
|
98
|
+
@name = options['name']
|
99
|
+
@desc = options['desc']
|
100
|
+
@type = options['type']
|
101
|
+
end
|
102
|
+
|
103
|
+
def to_s
|
104
|
+
inspect
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: caml
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ian Johnson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-11-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: "# \U0001F42A caml\n\n> Build CLI apps with YAML\n\n`caml` allows you
|
28
|
+
to build command line applications using declarative yaml.\n`caml` aims to be like
|
29
|
+
`make`, but by using descriptive, declarative yaml.\n\n## Usage\n\nRunning the command
|
30
|
+
without any arguments displays the commands defined in the `caml.yaml` file:\n\n```sh\nbin/caml\n```\n\n##
|
31
|
+
Declaring commands\n\n`caml` reads a file called `caml.yaml` in the current directory
|
32
|
+
and converts those commands into a unified CLI command.\n\nThe basic structure is
|
33
|
+
to have a `command`, which has a `desc` and an `execute` for the bash command to
|
34
|
+
execute.\n\n```yaml\ncommand:\n desc: Command description\n execute: script.sh\n```\n\nThis
|
35
|
+
yaml will create the following command:\n\n```yaml\nbin/caml command # Command description\n```\n\nAnd
|
36
|
+
it will run any bash command defined.\n\nArguments may be added under `args` in
|
37
|
+
a nested fashion as displayed below.\n\n```yaml\ncommand:\n args:\n one:\n desc:
|
38
|
+
First argument\n type: string\n two:\n desc: Second argument\n type:
|
39
|
+
string\n```\n\n## Examples\n\n```yaml\nbuild:\n desc: Build our project\n execute:
|
40
|
+
make\nclean:\n desc: Clean our project\n execute: make clean\n```\n\n```yaml\nbuild:\n
|
41
|
+
\ desc: Bundle\n execute: bundle install\nmigrate:\n desc: Migrate the test database\n
|
42
|
+
\ execute: rails db:migrate RAILS_ENV=test\ntest:\n desc: Run tests\n execute:
|
43
|
+
rspec\n```\n"
|
44
|
+
email: tacoda@hey.com
|
45
|
+
executables:
|
46
|
+
- caml
|
47
|
+
extensions: []
|
48
|
+
extra_rdoc_files: []
|
49
|
+
files:
|
50
|
+
- LICENSE
|
51
|
+
- README.md
|
52
|
+
- bin/caml
|
53
|
+
- lib/caml/caml.rb
|
54
|
+
- spec/caml/caml_spec.rb
|
55
|
+
homepage: https://github.com/tacoda/caml
|
56
|
+
licenses:
|
57
|
+
- MIT
|
58
|
+
metadata: {}
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '1.9'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubygems_version: 3.2.22
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: Build CLI apps using YAML
|
78
|
+
test_files:
|
79
|
+
- spec/caml/caml_spec.rb
|