thot 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +18 -0
- data/Gemfile +0 -1
- data/README.md +86 -29
- data/VERSION +1 -1
- data/assets/images/logo_thot_full_large.png +0 -0
- data/assets/images/logo_thot_light_large.png +0 -0
- data/assets/images/logo_thot_normal_large.png +0 -0
- data/exe/thot +46 -19
- data/lib/dependencies.rb +11 -0
- data/lib/thot/cli.rb +26 -29
- data/lib/thot/processor.rb +24 -0
- data/lib/thot/rake/manage.rb +20 -0
- data/lib/thot/rake/tasks/template.task +13 -0
- data/lib/thot/template.rb +127 -0
- data/lib/thot/tokenizer.rb +39 -0
- data/lib/thot/varfiles.rb +33 -0
- data/lib/thot.rb +1 -123
- data/samples/.thot.env +5 -0
- data/samples/template.ttl +21 -0
- data/thot.gemspec +3 -0
- metadata +43 -4
- data/samples/template.txt +0 -1
- data/samples/template2.txt +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b8633ef1b1cb491681966a9cb0439d1e3d8bb69766291623757b0f10fb13ed27
|
4
|
+
data.tar.gz: b7f7297728de329b92fcb064aeeff5d92249b29d9aeb5c430a0a4cf767a4829c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cda6359c5666cf77c09bd903217a98120ed994a1bc3993409f4decc4781751b5fee290fcb8eb02afdc5cce2fe0de0cc7352be4364338640d59a920ac17ff4e32
|
7
|
+
data.tar.gz: 256758ca6758aedfdf49dbd70ac73e1b48933b6f037a89b974ec939fae91820c61676476d698633e1a34bc3ed500f3d5dea6ba864c3b2daa824f35723102d429
|
@@ -0,0 +1,18 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on: [push,pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
steps:
|
9
|
+
- uses: actions/checkout@v2
|
10
|
+
- name: Set up Ruby
|
11
|
+
uses: ruby/setup-ruby@v1
|
12
|
+
with:
|
13
|
+
ruby-version: 3.0.0
|
14
|
+
- name: Run the default task
|
15
|
+
run: |
|
16
|
+
gem install bundler -v 2.2.3
|
17
|
+
bundle install
|
18
|
+
bundle exec rake
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,6 +2,26 @@
|
|
2
2
|
|
3
3
|
Thot is THe Operative Templating : the simpliest solution for Ruby and command to templatize
|
4
4
|
|
5
|
+

|
6
|
+
|
7
|
+
[](https://rubydoc.info/gems/thot)
|
8
|
+

|
9
|
+

|
10
|
+

|
11
|
+

|
12
|
+
|
13
|
+

|
14
|
+
[](https://badge.fury.io/rb/thot)
|
15
|
+

|
16
|
+

|
17
|
+

|
18
|
+
|
19
|
+
<noscript><a href="https://liberapay.com/ruydiaz/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript>
|
20
|
+
|
21
|
+
|
22
|
+

|
23
|
+
_The Operative Templating_
|
24
|
+
|
5
25
|
## Installation
|
6
26
|
|
7
27
|
Add this line to your application's Gemfile:
|
@@ -22,14 +42,16 @@ Or install it yourself as:
|
|
22
42
|
## Principe
|
23
43
|
|
24
44
|
Thot is a simple templating tool, with :
|
25
|
-
- a template including token, like :
|
45
|
+
- a template including token, like : **%%TOKEN_NAME%%** => Token MUST be in uppercase
|
26
46
|
- a hash of data (symbols as keys) corresponding, like : <pre>{token_name: 'value'}</pre>
|
27
47
|
It could generate an output.
|
28
48
|
|
29
|
-
|
49
|
+
From versions upper than 1.2.0, Thot support token syntax like **{{TOKEN_NAME}}**
|
50
|
+
|
51
|
+
### Simple usecase
|
30
52
|
|
31
53
|
- with data : <pre>{name: 'Romain'}</pre>
|
32
|
-
- and template content : "Hello
|
54
|
+
- and template content : "Hello **%%NAME%%** !"
|
33
55
|
|
34
56
|
Thot simply generate :
|
35
57
|
'Hello Romain !'
|
@@ -37,23 +59,50 @@ Thot simply generate :
|
|
37
59
|
### Advanced usecase
|
38
60
|
|
39
61
|
- with data : <pre>{firstname: 'romain', name: 'georges', nickname: 'zaidyur'}</pre>
|
40
|
-
- and template content : "Hello
|
62
|
+
- and template content : "Hello **%%FIRSTNAME.capitalize%%** **%%NAME.upcase%%** your nickname is : **%%NICKNAME.reverse.capitalize%%** !"
|
41
63
|
|
42
64
|
Thot generate :
|
43
65
|
"Hello Romain GEORGES your nickname is : Ruydiaz !"
|
44
66
|
|
67
|
+
This usecase use filters, see it in the following chapter.
|
68
|
+
**Note** : Your could monkey patch String or use Refinment for implementing our own filters.
|
69
|
+
|
70
|
+
|
71
|
+
### Thot templating Language (TTL) reference
|
72
|
+
|
73
|
+
- Token cloud be construct with {{TOKEN}} or %%TOKEN%%.
|
74
|
+
- Token must include filtering methods : Thot actually supports String to String piped filters
|
75
|
+
- Filters don't support parameters
|
76
|
+
- Filters must be stacked seperated by '.'
|
77
|
+
- Filters must be in lowercase
|
78
|
+
- Filters must be String instance methods returning a String (Modifier)
|
79
|
+
- Token should have default value
|
80
|
+
- Default values don't support multiline correctly.
|
81
|
+
|
82
|
+
This is some examples of correct TTL syntaxes :
|
83
|
+
|
84
|
+
With %%TOKEN%% :
|
85
|
+
- filters alone : %%NAME.capitalize%%
|
86
|
+
- stacked filters alone : %%SURNAME.upcase.reverse%%
|
87
|
+
- token only with default value : %%TOTO(default value)%%
|
88
|
+
- token only : %%NAME%%
|
89
|
+
- with filters with default value : %%TOTO.downcase(default value static)%%
|
90
|
+
- stacked filters with default value : %%SURNAME.upcase.reverse(default)%%
|
91
|
+
|
45
92
|
|
46
|
-
|
47
|
-
- filters
|
48
|
-
- filters
|
49
|
-
-
|
93
|
+
with {{TOKEN}} :
|
94
|
+
- filters alone: {{NAME.capitalize}}
|
95
|
+
- stacked filters alone : {{SURNAME.upcase.reverse}}
|
96
|
+
- token only with default value : {{TOTO(default value)}}
|
97
|
+
- token only : {{NAME}}
|
98
|
+
- with filters with default value : {{TOTO.downcase(default value static)}}
|
99
|
+
- stacked filters with default value : {{SURNAME.upcase.reverse(default)}}
|
50
100
|
|
51
|
-
Note : Your could monkey patch String or use Refinment for implementing our own filters.
|
52
101
|
|
53
102
|
|
54
103
|
## Usage
|
55
104
|
|
56
|
-
Thot is
|
105
|
+
Thot is a library for you usage AND a CLI tool.
|
57
106
|
|
58
107
|
### Ruby Library usage
|
59
108
|
|
@@ -61,7 +110,7 @@ you could use Thot in your Ruby code :
|
|
61
110
|
|
62
111
|
#### Strict mode and accessor input
|
63
112
|
|
64
|
-
Note : Considering 'template.txt' with : 'Hello
|
113
|
+
Note : Considering 'template.txt' with : 'Hello **%%NAME%%** !!'
|
65
114
|
Note : in strict mode if the Tokens in template file don't match exactly the given token list, Thot raise an exception.
|
66
115
|
|
67
116
|
```ruby
|
@@ -70,7 +119,7 @@ Note : in strict mode if the Tokens in template file don't match exactly the giv
|
|
70
119
|
template = Template::new list_token: [:name] , template_file: './template.txt'
|
71
120
|
template.name = 'Romain'
|
72
121
|
puts template.output
|
73
|
-
|
122
|
+
```
|
74
123
|
|
75
124
|
return
|
76
125
|
|
@@ -82,10 +131,10 @@ return
|
|
82
131
|
```ruby
|
83
132
|
require 'thot'
|
84
133
|
include Thot
|
85
|
-
template = Template::new list_token: [:name, :surname] , template_content: 'Hello %%
|
134
|
+
template = Template::new list_token: [:name, :surname] , template_content: 'Hello %%NAM%% !!'
|
86
135
|
template.name = 'Romain'
|
87
136
|
puts template.output
|
88
|
-
|
137
|
+
```
|
89
138
|
|
90
139
|
return
|
91
140
|
|
@@ -96,10 +145,10 @@ return
|
|
96
145
|
```ruby
|
97
146
|
require 'thot'
|
98
147
|
include Thot
|
99
|
-
template = Template::new list_token: [:name, :surname] , template_content: 'Hello %%NAME !!'
|
148
|
+
template = Template::new list_token: [:name, :surname] , template_content: 'Hello %%NAME%% !!'
|
100
149
|
template.map {name: 'Romain', surname: 'Georges' }
|
101
150
|
puts template.output
|
102
|
-
|
151
|
+
```
|
103
152
|
|
104
153
|
return
|
105
154
|
|
@@ -109,17 +158,31 @@ return
|
|
109
158
|
|
110
159
|
### CLI usage
|
111
160
|
|
112
|
-
Thot come with a CLI for templating :
|
161
|
+
Thot come with a CLI for templating, you could :
|
113
162
|
- reading from STDIN or list files arguments
|
114
|
-
- getting values from variables file by argument
|
163
|
+
- getting values from variables file by argument --env-var-file, -f FILENAME or from Thot Varfile, see after
|
115
164
|
- display output on STDOUT
|
116
165
|
- verbose mode on STDERR if -v options.
|
166
|
+
- defining environment with --environment, -e ENV (efault environement is :development)
|
167
|
+
- debug mode on STDERR if -d options (cumulative with verbose).
|
168
|
+
|
169
|
+
Note : the Thot CLI look for ~/.thot.env file or ./.thot.env file
|
170
|
+
|
171
|
+
This file support INI format or flat format, keys in INI format are used for environment override
|
172
|
+
|
173
|
+
|
174
|
+
|
175
|
+
Note : CLI work only strict mode false, you could have unused keys in datas and undefined value for tokens (substitute by '').
|
176
|
+
|
177
|
+
Order for variable priorities :
|
178
|
+
- ~/.thot.env
|
179
|
+
- ./.thot.env
|
180
|
+
- file passed by --env-var-file
|
117
181
|
|
118
|
-
Note : CLI work only strict mode false, you could have unused keys in datas.
|
119
182
|
|
120
183
|
#### Pre-requisites
|
121
184
|
|
122
|
-
* a file 'template.
|
185
|
+
* a file 'template.ttl' with : "Hello **%%NAME%%** !!"
|
123
186
|
* a variables file with lines, like :
|
124
187
|
```
|
125
188
|
key=value
|
@@ -139,25 +202,19 @@ In the same path
|
|
139
202
|
#### STDIN from echo
|
140
203
|
|
141
204
|
```
|
142
|
-
$ echo "Hello %%NAME%% !!" |thot -
|
205
|
+
$ echo "Hello %%NAME%% !!" |thot -f env.test
|
143
206
|
```
|
144
207
|
|
145
208
|
#### STDIN from input
|
146
209
|
|
147
210
|
```
|
148
|
-
$ thot -
|
149
|
-
```
|
150
|
-
|
151
|
-
#### Files list
|
152
|
-
|
153
|
-
```
|
154
|
-
$ thot -e env.test template1.txt template2.txt
|
211
|
+
$ thot -f env.test < template.ttl
|
155
212
|
```
|
156
213
|
|
157
214
|
#### Typical usage
|
158
215
|
|
159
216
|
```
|
160
|
-
$ thot -
|
217
|
+
$ thot -f env.test < template.ttl > output.txt
|
161
218
|
```
|
162
219
|
|
163
220
|
###
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.2.0
|
Binary file
|
Binary file
|
Binary file
|
data/exe/thot
CHANGED
@@ -1,30 +1,57 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
|
4
|
+
require 'carioca'
|
5
|
+
|
6
|
+
Carioca::Registry.configure do |spec|
|
7
|
+
spec.debug = false
|
8
|
+
spec.init_from_file = false
|
9
|
+
spec.output_mode = :mono
|
10
|
+
spec.output_emoji = true
|
11
|
+
spec.output_colors = true
|
12
|
+
spec.output_target = STDERR
|
13
|
+
end
|
14
|
+
|
4
15
|
require 'optparse'
|
5
16
|
require 'thot'
|
6
17
|
require 'thot/cli'
|
7
18
|
|
8
|
-
|
9
|
-
OptionParser.new do |opts|
|
10
|
-
opts.banner = "Usage: thot [options]"
|
11
|
-
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
12
|
-
options[:verbose] = v
|
13
|
-
end
|
14
|
-
opts.on("-eFILENAME", "--env-var-file=FILENAME", "[MANDATORY] Environment variables file (key=value pairs by line)") do |file|
|
15
|
-
options[:env_var_file] = file
|
16
|
-
end
|
17
|
-
end.parse!
|
18
|
-
|
19
|
-
list_input = ARGV.dup
|
20
|
-
ARGV.clear
|
21
|
-
|
22
|
-
|
23
|
-
include Thot
|
24
|
-
|
25
|
-
cli = CLI::new options: options, list_templates_file: list_input
|
26
|
-
cli.generate
|
19
|
+
output = Carioca::Registry::init.get_service name: :output
|
27
20
|
|
21
|
+
options = {}
|
22
|
+
options[:environment] = :development
|
23
|
+
begin
|
24
|
+
OptionParser.new do |opts|
|
25
|
+
opts.banner = "Usage: thot [options]"
|
26
|
+
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
27
|
+
options[:verbose] = v
|
28
|
+
end
|
29
|
+
opts.on("-d", "--[no-]debug", "Run on debug") do |d|
|
30
|
+
options[:debug] = d
|
31
|
+
end
|
32
|
+
opts.on("-f FILENAME", "--env-var-file=FILENAME", "Environment variables file (key=value pairs by line)") do |file|
|
33
|
+
options[:env_var_file] = file
|
34
|
+
end
|
35
|
+
opts.on("-e ENVIRONMENT", "--environment=ENVIRONMENT", "Environment name") do |evt|
|
36
|
+
options[:environment] = evt
|
37
|
+
end
|
38
|
+
end.parse!
|
39
|
+
|
40
|
+
list_input = ARGV.dup
|
41
|
+
ARGV.clear
|
42
|
+
|
43
|
+
include Thot
|
44
|
+
cli = CLI::new options: options, template_file: list_input.first
|
45
|
+
cli.generate
|
46
|
+
rescue Interrupt => e
|
47
|
+
output.error "Execution interrupted"
|
48
|
+
rescue SignalException => e
|
49
|
+
output.error "Execution interrupted"
|
50
|
+
rescue Exception => e
|
51
|
+
output.error "Execution error : #{e.message}"
|
52
|
+
rescue OptionParser::MissingArgument => e
|
53
|
+
output.error "incorrect usage : #{e.message}"
|
54
|
+
end
|
28
55
|
|
29
56
|
|
30
57
|
|
data/lib/dependencies.rb
ADDED
data/lib/thot/cli.rb
CHANGED
@@ -1,9 +1,18 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
1
4
|
module Thot
|
2
|
-
class CLI
|
5
|
+
class CLI < Carioca::Container
|
6
|
+
|
7
|
+
inject service: :output
|
8
|
+
|
9
|
+
def initialize(options: , template_file: nil )
|
3
10
|
|
4
|
-
def initialize(options: , list_templates_file: nil )
|
5
|
-
@list_templates_file = list_templates_file
|
6
11
|
@options = options
|
12
|
+
output.level = (@options[:debug])? :debug : :info
|
13
|
+
output.debug "Debugging mode activated" if @options[:debug]
|
14
|
+
@template_file = template_file
|
15
|
+
output.info "Assuming Environment : #{@options[:environment]}" if @options[:verbose]
|
7
16
|
getting_data
|
8
17
|
getting_content
|
9
18
|
end
|
@@ -12,7 +21,7 @@ module Thot
|
|
12
21
|
def generate
|
13
22
|
template = Template::new(list_token: @data.keys, template_content: @content, strict: false)
|
14
23
|
template.map @data
|
15
|
-
|
24
|
+
output.info "Generating output" if @options[:verbose]
|
16
25
|
puts template.output
|
17
26
|
end
|
18
27
|
|
@@ -20,43 +29,31 @@ module Thot
|
|
20
29
|
|
21
30
|
def getting_data
|
22
31
|
if @options[:env_var_file] then
|
23
|
-
|
24
|
-
@data = read_evt_file(@options[:env_var_file])
|
32
|
+
output.info "Environment file given : #{@options[:env_var_file]}" if @options[:verbose]
|
25
33
|
else
|
26
|
-
|
34
|
+
output.info "Environment variables file argument missing, (--env-var-file) " if @options[:verbose]
|
27
35
|
end
|
36
|
+
@data = Varfiles::new(environment: @options[:environment], varfile: @options[:env_var_file]).data
|
28
37
|
end
|
29
38
|
|
30
39
|
def getting_content
|
31
40
|
@content = ""
|
32
|
-
if @
|
33
|
-
|
41
|
+
if @template_file.nil?
|
42
|
+
output.info "Reading content from STDIN (CTRL+D to commit)" if STDIN.tty?
|
43
|
+
output.info "Getting content from STDIN" if @options[:verbose] and not STDIN.tty?
|
34
44
|
@content = ARGF.readlines.join
|
35
45
|
else
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
43
|
-
end
|
46
|
+
output.info "Reading content from file : #{@template_file}" if @options[:verbose]
|
47
|
+
if File::exist? @template_file
|
48
|
+
@content = File::readlines(@template_file).join
|
49
|
+
else
|
50
|
+
raise "file not found #{@template_file}"
|
51
|
+
end
|
44
52
|
end
|
45
53
|
end
|
46
54
|
|
47
55
|
def read_evt_file(file)
|
48
|
-
|
49
|
-
if File::exist? file
|
50
|
-
content = File::readlines(file)
|
51
|
-
else
|
52
|
-
raise "Environment variables file not found #{file}"
|
53
|
-
end
|
54
|
-
content.each do |line|
|
55
|
-
next if line =~ /#/
|
56
|
-
key,value = line.split('=')
|
57
|
-
res[key.strip.to_sym] = value.strip if value
|
58
|
-
end
|
59
|
-
return res
|
56
|
+
return
|
60
57
|
end
|
61
58
|
|
62
59
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Thot
|
2
|
+
class Processor
|
3
|
+
attr_reader :value
|
4
|
+
attr_reader :rule
|
5
|
+
|
6
|
+
def initialize(value: , rule:)
|
7
|
+
@value = value
|
8
|
+
@rule = rule
|
9
|
+
end
|
10
|
+
|
11
|
+
def result
|
12
|
+
if @value.nil? then
|
13
|
+
result = (@rule[:default])? @rule[:default] : ''
|
14
|
+
else
|
15
|
+
result = @value
|
16
|
+
@rule[:filters].each do |filter|
|
17
|
+
result = result.send filter.to_sym if result.respond_to? filter.to_sym
|
18
|
+
end
|
19
|
+
end
|
20
|
+
return result
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'thot'
|
4
|
+
|
5
|
+
$VERBOSE = nil
|
6
|
+
if Gem::Specification.respond_to?(:find_by_name)
|
7
|
+
begin
|
8
|
+
spec = Gem::Specification.find_by_name('thot')
|
9
|
+
res = spec.lib_dirs_glob.split('/')
|
10
|
+
rescue LoadError
|
11
|
+
res = []
|
12
|
+
end
|
13
|
+
else
|
14
|
+
spec = Gem.searcher.find('carioca')
|
15
|
+
res = Gem.searcher.lib_dirs_for(spec).split('/')
|
16
|
+
end
|
17
|
+
|
18
|
+
res.pop
|
19
|
+
tasks_path = res.join('/').concat('/lib/thot/rake/tasks/')
|
20
|
+
Dir["#{tasks_path}/*.task*"].each { |ext| load ext }
|
@@ -0,0 +1,13 @@
|
|
1
|
+
namespace :thot do
|
2
|
+
|
3
|
+
desc "templatize file from ENV vars : THOT_VARFILE, THOT_TEMPLATE "
|
4
|
+
task :templatize do
|
5
|
+
|
6
|
+
if ENV.include? 'THOT_VARFILE' and ENV.include? 'THOT_VARFILE'
|
7
|
+
system("thot -f #{ENV['THOT_VARFILE']} #{ENV['THOT_TEMPLATE']}")
|
8
|
+
else
|
9
|
+
puts 'Please precise THOT_VARFILE and THOT_TEMPLATE environment variables'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# Thot base module
|
2
|
+
module Thot
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
|
6
|
+
# KISS template Engine
|
7
|
+
class Template
|
8
|
+
|
9
|
+
# getter of the list of token
|
10
|
+
attr_reader :list_token
|
11
|
+
# getter of the template file
|
12
|
+
attr_reader :template_file
|
13
|
+
# getter of the flat content of the template
|
14
|
+
attr_reader :content
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
# constructor : generate the pseudo accessor for template Class from token list
|
20
|
+
def initialize(template_file: nil, list_token: , strict: true, template_content: nil)
|
21
|
+
@myput = Carioca::Registry::init.get_service name: :output if defined?(Carioca::Injector)
|
22
|
+
@result = ""
|
23
|
+
if template_file
|
24
|
+
@template_file = template_file
|
25
|
+
raise NoTemplateFile::new('No template file found') unless File::exist?(@template_file)
|
26
|
+
begin
|
27
|
+
@content = IO::readlines(@template_file).join.chomp
|
28
|
+
rescue
|
29
|
+
raise NoTemplateFile::new('Template file read error')
|
30
|
+
end
|
31
|
+
elsif template_content
|
32
|
+
@content = template_content
|
33
|
+
else
|
34
|
+
raise NoTemplateFile::new('No template file found or template content')
|
35
|
+
end
|
36
|
+
|
37
|
+
@tokenizer = Tokenizer::new string: @content ; @tokenizer.detect
|
38
|
+
token_from_template = @tokenizer.tokens
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
begin
|
43
|
+
@list_token = list_token
|
44
|
+
@hash_token = Hash::new; @list_token.each{|_item| @hash_token[_item.to_s] = String::new('')}
|
45
|
+
rescue
|
46
|
+
raise InvalidTokenList::new("Token list malformation")
|
47
|
+
end
|
48
|
+
if strict
|
49
|
+
raise InvalidTokenList::new("Token list doesn't match the template") unless token_from_template.sort == @list_token.sort
|
50
|
+
end
|
51
|
+
if @myput then
|
52
|
+
if @myput.level == :debug then
|
53
|
+
@myput.debug "Template :"
|
54
|
+
@content.split("\n").each do |line|
|
55
|
+
@myput.debug " #{line}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
@list_token.each do |_token|
|
60
|
+
self.instance_eval do
|
61
|
+
define_singleton_method(:"#{_token}=") {|_value| raise ArgumentError::new('Not a String') unless _value.class == String; @hash_token[__callee__.to_s.chomp('=')] = _value }
|
62
|
+
define_singleton_method(_token.to_sym) { return @hash_token[__callee__.to_s] }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
# generic accessor
|
69
|
+
# @param [Symbol] _token in the token list
|
70
|
+
# @param [String] _value a text value
|
71
|
+
# @raise [ArgumentError] if _valu is not a String
|
72
|
+
def token(_token,_value)
|
73
|
+
raise ArgumentError::new('Not a String') unless _value.class == String
|
74
|
+
@hash_token[_token.to_s] = _value
|
75
|
+
end
|
76
|
+
|
77
|
+
# map a hash against templates token_list
|
78
|
+
# @param [Hash] _hash a hash data to map
|
79
|
+
def map(_hash)
|
80
|
+
_data = {}
|
81
|
+
_hash.each { |item,val|
|
82
|
+
raise ArgumentError::new("#{item} : Not a String") unless val.class == String
|
83
|
+
_data[item.to_s.downcase] = val
|
84
|
+
}
|
85
|
+
@hash_token = _data
|
86
|
+
end
|
87
|
+
|
88
|
+
# collector for pseudo accessor to prevent bad mapping
|
89
|
+
# @raise [NotAToken] if caling an accessor not mapped in token list
|
90
|
+
def method_missing(_name,*_args)
|
91
|
+
raise NotAToken
|
92
|
+
end
|
93
|
+
|
94
|
+
# the templater himself : proceed to templating
|
95
|
+
# @return [String] the template output
|
96
|
+
def output
|
97
|
+
@result = @content
|
98
|
+
|
99
|
+
@tokenizer.definitions.each do |item, rule|
|
100
|
+
@result.gsub!(item, Thot::Processor::new(value: @hash_token[rule[:key].downcase], rule: rule).result)
|
101
|
+
end
|
102
|
+
|
103
|
+
if @myput then
|
104
|
+
if @myput.level == :debug then
|
105
|
+
@myput.debug "Output :"
|
106
|
+
@result.split("\n").each do |line|
|
107
|
+
@myput.debug " #{line}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
return @result
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
# Exception for an invalid Token list
|
117
|
+
class InvalidTokenList < Exception; end
|
118
|
+
# Exception for an malformed token
|
119
|
+
class NotAToken < Exception; end
|
120
|
+
# Exception for an invalid template file
|
121
|
+
class NoTemplateFile < Exception; end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Thot
|
2
|
+
class Tokenizer
|
3
|
+
|
4
|
+
attr_reader :string
|
5
|
+
attr_reader :tokens
|
6
|
+
attr_reader :definitions
|
7
|
+
|
8
|
+
def initialize(string: )
|
9
|
+
@string = string
|
10
|
+
@definitions = {}
|
11
|
+
@tokens = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def detect
|
15
|
+
detected = @string.scan(/%%[^\%]*%%/).concat @string.scan(/\{\{[^\}]*\}\}/)
|
16
|
+
detected.each do |token|
|
17
|
+
key,*rest = token[2,(token.length - 4)].split('.')
|
18
|
+
filters = []; default = nil
|
19
|
+
if key =~ /(.*)\((.*)\)/ then
|
20
|
+
key = $1
|
21
|
+
default = $2
|
22
|
+
end
|
23
|
+
rest.each {|item|
|
24
|
+
if item =~ /(.*)\((.*)\)/ then
|
25
|
+
filters.push $1
|
26
|
+
default = $2
|
27
|
+
|
28
|
+
else
|
29
|
+
filters.push item
|
30
|
+
end
|
31
|
+
}
|
32
|
+
@tokens.push key unless @tokens.include? key
|
33
|
+
@definitions[token] = {key: key, filters: filters , default: default }
|
34
|
+
end
|
35
|
+
@tokens.map!(&:downcase)
|
36
|
+
@tokens.map!(&:to_sym)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Thot
|
2
|
+
class Varfiles
|
3
|
+
|
4
|
+
extend Carioca::Injector if defined?(Carioca::Injector)
|
5
|
+
attr_reader :data
|
6
|
+
inject service: :output if self.respond_to? :inject
|
7
|
+
|
8
|
+
def initialize(varfile: nil, environment: nil, dotfiles: ["./.thot.env","~/.thot.env"])
|
9
|
+
@name = self.class
|
10
|
+
@data = {}
|
11
|
+
scanned_files = dotfiles
|
12
|
+
scanned_files.push varfile unless varfile.nil?
|
13
|
+
scanned_files.each do |file|
|
14
|
+
real_file = File.expand_path(file)
|
15
|
+
if File::exists? real_file then
|
16
|
+
output.debug "Negociated files : #{real_file}, merging..." if self.respond_to?(:output)
|
17
|
+
datafile = IniFile.load(real_file)
|
18
|
+
@data.merge! datafile["global"]
|
19
|
+
@data.merge! datafile[environment] if datafile.sections.include? environment.to_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
@data.transform_keys!(&:to_sym)
|
23
|
+
if self.respond_to?(:output) then
|
24
|
+
if output.level == :debug then
|
25
|
+
output.debug "merged data:"
|
26
|
+
@data.each do |key,val|
|
27
|
+
output.debug "* #{key} = #{val}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/thot.rb
CHANGED
@@ -1,125 +1,3 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
|
2
|
+
require_relative "dependencies"
|
3
3
|
|
4
|
-
# Thot base module
|
5
|
-
module Thot
|
6
|
-
class Error < StandardError; end
|
7
|
-
|
8
|
-
|
9
|
-
# KISS template Engine
|
10
|
-
class Template
|
11
|
-
|
12
|
-
# getter of the list of token
|
13
|
-
attr_reader :list_token
|
14
|
-
# getter of the template file
|
15
|
-
attr_reader :template_file
|
16
|
-
# getter of the flat content of the template
|
17
|
-
attr_reader :content
|
18
|
-
|
19
|
-
# constructor : generate the pseudo accessor for template Class from token list
|
20
|
-
def initialize(template_file: nil, list_token: , strict: true, template_content: nil)
|
21
|
-
|
22
|
-
@result = ""
|
23
|
-
if template_file
|
24
|
-
@template_file = template_file
|
25
|
-
raise NoTemplateFile::new('No template file found') unless File::exist?(@template_file)
|
26
|
-
begin
|
27
|
-
@content = IO::readlines(@template_file).join.chomp
|
28
|
-
rescue
|
29
|
-
raise NoTemplateFile::new('Template file read error')
|
30
|
-
end
|
31
|
-
elsif template_content
|
32
|
-
@content = template_content
|
33
|
-
else
|
34
|
-
raise NoTemplateFile::new('No template file found or template content')
|
35
|
-
end
|
36
|
-
|
37
|
-
|
38
|
-
token_from_template = @content.scan(/%%(\w+)%%/).flatten.uniq.map{ |item| item.downcase.to_sym}
|
39
|
-
begin
|
40
|
-
@list_token = list_token
|
41
|
-
@hash_token = Hash::new; @list_token.each{|_item| @hash_token[_item.to_s] = String::new('')}
|
42
|
-
rescue
|
43
|
-
raise InvalidTokenList::new("Token list malformation")
|
44
|
-
end
|
45
|
-
if strict
|
46
|
-
raise InvalidTokenList::new("Token list doesn't match the template") unless token_from_template.sort == @list_token.sort
|
47
|
-
else
|
48
|
-
raise InvalidTokenList::new("Token list doesn't match the template") unless (token_from_template.sort & @list_token.sort) == token_from_template.sort
|
49
|
-
end
|
50
|
-
@list_token.each do |_token|
|
51
|
-
self.instance_eval do
|
52
|
-
define_singleton_method(:"#{_token}=") {|_value| raise ArgumentError::new('Not a String') unless _value.class == String; @hash_token[__callee__.to_s.chomp('=')] = _value }
|
53
|
-
define_singleton_method(_token.to_sym) { return @hash_token[__callee__.to_s] }
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
# generic accessor
|
60
|
-
# @param [Symbol] _token in the token list
|
61
|
-
# @param [String] _value a text value
|
62
|
-
# @raise [ArgumentError] if _valu is not a String
|
63
|
-
def token(_token,_value)
|
64
|
-
raise ArgumentError::new('Not a String') unless _value.class == String
|
65
|
-
@hash_token[_token.to_s] = _value
|
66
|
-
end
|
67
|
-
|
68
|
-
# map a hash against templates token_list
|
69
|
-
# @param [Hash] _hash a hash data to map
|
70
|
-
def map(_hash)
|
71
|
-
_data = {}
|
72
|
-
_hash.each { |item,val|
|
73
|
-
raise ArgumentError::new("#{item} : Not a String") unless val.class == String
|
74
|
-
_data[item.to_s.downcase] = val
|
75
|
-
}
|
76
|
-
@hash_token = _data
|
77
|
-
end
|
78
|
-
|
79
|
-
# collector for pseudo accessor to prevent bad mapping
|
80
|
-
# @raise [NotAToken] if caling an accessor not mapped in token list
|
81
|
-
def method_missing(_name,*_args)
|
82
|
-
raise NotAToken
|
83
|
-
end
|
84
|
-
|
85
|
-
# the templater;proceed to templating
|
86
|
-
# @return [String] the template output
|
87
|
-
def output
|
88
|
-
@result = @content
|
89
|
-
@list_token.each{|_token|
|
90
|
-
self.filtering @content.scan(/%%(#{_token.to_s.upcase}[\.\w+]+)%%/).flatten
|
91
|
-
@result.gsub!(/%%#{_token.to_s.upcase}%%/,@hash_token[_token.to_s]) if @hash_token.include? _token.to_s
|
92
|
-
}
|
93
|
-
return @result
|
94
|
-
end
|
95
|
-
|
96
|
-
private
|
97
|
-
|
98
|
-
def filtering(list)
|
99
|
-
@filtered_tokens = {}
|
100
|
-
list.each do |pipe|
|
101
|
-
token, *filters = pipe.split('.')
|
102
|
-
@filtered_tokens[pipe] = @hash_token[token.downcase]
|
103
|
-
filters.each do |filter|
|
104
|
-
@filtered_tokens[pipe] = @filtered_tokens[pipe].send filter.to_sym if @filtered_tokens[pipe].respond_to? filter.to_sym
|
105
|
-
end
|
106
|
-
end
|
107
|
-
@filtered_tokens.each do |item,value|
|
108
|
-
@result.gsub!(/%%#{item}%%/,value)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
|
113
|
-
end
|
114
|
-
|
115
|
-
# Exception for an invalid Token list
|
116
|
-
class InvalidTokenList < Exception; end
|
117
|
-
# Exception for an malformed token
|
118
|
-
class NotAToken < Exception; end
|
119
|
-
# Exception for an invalid template file
|
120
|
-
class NoTemplateFile < Exception; end
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
|
125
|
-
|
data/samples/.thot.env
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
With %
|
2
|
+
filters alone : %%NAME.capitalize%% %%SURNAME.upcase%%
|
3
|
+
stacked filters alone : %%SURNAME.upcase.reverse%%
|
4
|
+
no tokens
|
5
|
+
empty with default value : %%TOTO(default value)%%
|
6
|
+
only defined token : %%NAME%%
|
7
|
+
empty with filters with default value : %%TOTO.downcase(default value static)%%
|
8
|
+
empty : %%TOTO%%
|
9
|
+
defined stacked filters with default value : %%SURNAME.upcase.reverse(default)%%
|
10
|
+
empty stacked filters with default value : %%TOTO.upcase.reverse(default)%%
|
11
|
+
|
12
|
+
with {
|
13
|
+
filters alone: {{NAME.capitalize}} {{SURNAME.upcase}}
|
14
|
+
stacked filters alone : {{SURNAME.upcase.reverse}}
|
15
|
+
no tokens
|
16
|
+
empty with default value : {{TOTO(default value)}}
|
17
|
+
only defined token : {{NAME}}
|
18
|
+
empty with filters with default value : {{TOTO.downcase(default value static)}}
|
19
|
+
empty : {{TOTO}}
|
20
|
+
defined stacked filters with default value : {{SURNAME.upcase.reverse(default)}}
|
21
|
+
empty stacked filters with default value : {{TOTO.upcase.reverse(default)}}
|
data/thot.gemspec
CHANGED
@@ -22,6 +22,9 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
23
|
spec.require_paths = ["lib"]
|
24
24
|
|
25
|
+
spec.add_dependency "carioca", "~> 2.0"
|
26
|
+
spec.add_dependency "inifile", "~> 3.0"
|
27
|
+
|
25
28
|
spec.add_development_dependency 'rake', '~> 12.0'
|
26
29
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
27
30
|
spec.add_development_dependency 'rubocop', '~> 1.32'
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Romain GEORGES
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: carioca
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: inifile
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: rake
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,6 +158,7 @@ executables:
|
|
130
158
|
extensions: []
|
131
159
|
extra_rdoc_files: []
|
132
160
|
files:
|
161
|
+
- ".github/workflows/ruby.yml"
|
133
162
|
- ".gitignore"
|
134
163
|
- ".rspec"
|
135
164
|
- ".rubocop.yml"
|
@@ -138,15 +167,25 @@ files:
|
|
138
167
|
- README.md
|
139
168
|
- Rakefile
|
140
169
|
- VERSION
|
170
|
+
- assets/images/logo_thot_full_large.png
|
171
|
+
- assets/images/logo_thot_light_large.png
|
172
|
+
- assets/images/logo_thot_normal_large.png
|
141
173
|
- bin/console
|
142
174
|
- bin/setup
|
143
175
|
- exe/thot
|
176
|
+
- lib/dependencies.rb
|
144
177
|
- lib/thot.rb
|
145
178
|
- lib/thot/cli.rb
|
179
|
+
- lib/thot/processor.rb
|
180
|
+
- lib/thot/rake/manage.rb
|
181
|
+
- lib/thot/rake/tasks/template.task
|
182
|
+
- lib/thot/template.rb
|
183
|
+
- lib/thot/tokenizer.rb
|
184
|
+
- lib/thot/varfiles.rb
|
146
185
|
- lib/thot/version.rb
|
147
186
|
- samples/.env.prod
|
148
|
-
- samples
|
149
|
-
- samples/
|
187
|
+
- samples/.thot.env
|
188
|
+
- samples/template.ttl
|
150
189
|
- thot.gemspec
|
151
190
|
- ultragreen_roodi_coding_convention.yml
|
152
191
|
homepage: https://github.com/Ultragreen/thot
|
data/samples/template.txt
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
Hello %%NAME.capitalize%% %%SURNAME.upcase%% !!
|
data/samples/template2.txt
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
Hello %%NAME.capitalize%% %%SURNAME.upcase%% !!
|