thot 1.1.0 → 1.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57ada5bc80f13298187297034ec1b9c754cdf0cd2313bd5a2674f8bc8ad2f165
4
- data.tar.gz: '0961751bbb5709516c88958ee50fee4e62c0802a9ce7b0d58c72bf1a2d544b1a'
3
+ metadata.gz: 8f6c6ae92314f63db63bf820e0256e0592733b7d0c0d57187d50926baefb82e0
4
+ data.tar.gz: 27a167400f4ece2859aafb3f0b159aca3ab5193fc7087336c6fe3b084a7d10a0
5
5
  SHA512:
6
- metadata.gz: bb39588c702fbc6ba92ffb46cc16dff45401f18bd8e82c68e10639f242b0ff571b3119b0ba3be83b83f212dffd6b354cfa1519a13603bba0a32e50e3da1d379f
7
- data.tar.gz: a6533192e004e53ea6a9f456a60f11b72fa4e57ff7c9840a19b7e75f7846fa9a8477560f0655f5d2c1ad5d9f3898e4c05c5a52e23124a3f4b9e808a519117782
6
+ metadata.gz: 6a5102b7469e1ac351626ae1efc577aaf5249b0c4ce5f3d0d48136dc1be11bf9bca4cb4ce0ee4fbfb10e8aa03dfda05f7ebbdda90a3e4a44e9253c2f9b4212fe
7
+ data.tar.gz: 2e73db783b15bf08c1660ea387402a39ff44079cbd05b25feca19ae5db83d23a7a7e6f3467c9ff96094fe3d6a07be9c32afe3a2c3bcdc79e03e65a9782890e8d
@@ -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
@@ -2,4 +2,3 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
-
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2022 Pierre Alphonse
3
+ Copyright (c) 2022 Romain GEORGES
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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
+ ![GitHub](https://img.shields.io/github/license/Ultragreen/thot)
6
+
7
+ [![Documentation](https://img.shields.io/badge/docs-rubydoc.info-brightgreen)](https://rubydoc.info/gems/thot)
8
+ ![GitHub issues](https://img.shields.io/github/issues/Ultragreen/thot)
9
+ ![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/Ultragreen/thot)
10
+ ![GitHub top language](https://img.shields.io/github/languages/top/Ultragreen/thot)
11
+ ![GitHub milestones](https://img.shields.io/github/milestones/open/Ultragreen/thot)
12
+
13
+ ![Gem](https://img.shields.io/gem/dt/thot)
14
+ [![Gem Version](https://badge.fury.io/rb/thot.svg)](https://badge.fury.io/rb/thot)
15
+ ![Twitter Follow](https://img.shields.io/twitter/follow/Ultragreen?style=social)
16
+ ![GitHub Org's stars](https://img.shields.io/github/stars/Ultragreen?style=social)
17
+ ![GitHub watchers](https://img.shields.io/github/watchers/Ultragreen/thot?style=social)
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
+ ![Thot logo](assets/images/logo_thot_full_large.png)
23
+ _The Operative Templating_
24
+
5
25
  ## Installation
6
26
 
7
27
  Add this line to your application's Gemfile:
@@ -22,14 +42,20 @@ 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 : %%TOKEN_NAME%% => Token MUST be in uppercase
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
- ### Usecase
49
+ From versions upper than 1.2.0, Thot support token syntax like **{{TOKEN_NAME}}**
50
+
51
+ ### Global synoptic
52
+
53
+ ![synoptic](assets/images/description_thot.png)
54
+
55
+ ### Simple usecase
30
56
 
31
57
  - with data : <pre>{name: 'Romain'}</pre>
32
- - and template content : "Hello %%NAME%% !"
58
+ - and template content : "Hello **%%NAME%%** !"
33
59
 
34
60
  Thot simply generate :
35
61
  'Hello Romain !'
@@ -37,31 +63,68 @@ Thot simply generate :
37
63
  ### Advanced usecase
38
64
 
39
65
  - with data : <pre>{firstname: 'romain', name: 'georges', nickname: 'zaidyur'}</pre>
40
- - and template content : "Hello %%FIRSTNAME.capitalize%% %%NAME.upcase%% your nickname is : %%NICKNAME.reverse.capitalize%% !"
66
+ - and template content : "Hello **%%FIRSTNAME.capitalize%%** **%%NAME.upcase%%** your nickname is : **%%NICKNAME.reverse.capitalize%%** !"
41
67
 
42
68
  Thot generate :
43
69
  "Hello Romain GEORGES your nickname is : Ruydiaz !"
44
70
 
71
+ This usecase use filters, see it in the following chapter.
72
+ **Note** : Your could monkey patch String or use Refinment for implementing our own filters.
73
+
74
+
75
+ ### Thot templating Language (TTL) reference
76
+
77
+ - Token cloud be construct with {{TOKEN}} or %%TOKEN%%.
78
+ - Token must include filtering methods : Thot actually supports String to String piped filters
79
+ - Filters don't support parameters
80
+ - Filters must be stacked seperated by '.'
81
+ - Filters must be in lowercase
82
+ - Filters must be String instance methods returning a String (Modifier)
83
+ - Token should have default value
84
+ - Default values don't support multiline correctly.
85
+
86
+ This is some examples of correct TTL syntaxes :
87
+
88
+ With %%TOKEN%% :
89
+ - filters alone : %%NAME.capitalize%%
90
+ - stacked filters alone : %%SURNAME.upcase.reverse%%
91
+ - token only with default value : %%TOTO(default value)%%
92
+ - token only : %%NAME%%
93
+ - with filters with default value : %%TOTO.downcase(default value static)%%
94
+ - stacked filters with default value : %%SURNAME.upcase.reverse(default)%%
45
95
 
46
- Thot actually supports String to String piped filters :
47
- - filters must be stacked seperated by '.'
48
- - filters must be in lowercase
49
- - filters must be String instance methods returning a String (Modifier)
50
96
 
51
- Note : Your could monkey patch String or use Refinment for implementing our own filters.
97
+ with {{TOKEN}} :
98
+ - filters alone: {{NAME.capitalize}}
99
+ - stacked filters alone : {{SURNAME.upcase.reverse}}
100
+ - token only with default value : {{TOTO(default value)}}
101
+ - token only : {{NAME}}
102
+ - with filters with default value : {{TOTO.downcase(default value static)}}
103
+ - stacked filters with default value : {{SURNAME.upcase.reverse(default)}}
104
+
52
105
 
53
106
 
54
107
  ## Usage
55
108
 
56
- Thot is already a library for you usage and a CLI.
109
+ Thot is a library for you usage AND a CLI tool.
57
110
 
58
111
  ### Ruby Library usage
59
112
 
113
+
114
+ #### Thot::Template
115
+
116
+
117
+ ##### Synoptic
118
+
119
+ ![synoptic](assets/images/description_thot_template.png)
120
+
60
121
  you could use Thot in your Ruby code :
61
122
 
62
- #### Strict mode and accessor input
123
+ ##### Examples
124
+
125
+ ###### Strict mode and accessor input
63
126
 
64
- Note : Considering 'template.txt' with : 'Hello %%NAME !!'
127
+ Note : Considering 'template.txt' with : 'Hello **%%NAME%%** !!'
65
128
  Note : in strict mode if the Tokens in template file don't match exactly the given token list, Thot raise an exception.
66
129
 
67
130
  ```ruby
@@ -70,56 +133,179 @@ Note : in strict mode if the Tokens in template file don't match exactly the giv
70
133
  template = Template::new list_token: [:name] , template_file: './template.txt'
71
134
  template.name = 'Romain'
72
135
  puts template.output
73
- ````
136
+ ```
74
137
 
75
138
  return
76
139
 
77
140
  Hello Romain !!
78
141
 
79
142
 
80
- #### Strict mode false with accesor input and template_content
143
+ ###### Strict mode false with accesor input and template_content
81
144
 
82
145
  ```ruby
83
146
  require 'thot'
84
147
  include Thot
85
- template = Template::new list_token: [:name, :surname] , template_content: 'Hello %%NAME !!'
148
+ template = Template::new list_token: [:name, :surname] , template_content: 'Hello %%NAM%% !!'
86
149
  template.name = 'Romain'
87
150
  puts template.output
88
- ````
151
+ ```
89
152
 
90
153
  return
91
154
 
92
155
  Hello Romain !!
93
156
 
94
- #### Strict mode false with map input and template_content
157
+ ###### Strict mode false with map input and template_content
95
158
 
96
159
  ```ruby
97
160
  require 'thot'
98
161
  include Thot
99
- template = Template::new list_token: [:name, :surname] , template_content: 'Hello %%NAME !!'
162
+ template = Template::new list_token: [:name, :surname] , template_content: 'Hello %%NAME%% !!'
100
163
  template.map {name: 'Romain', surname: 'Georges' }
101
164
  puts template.output
102
- ````
165
+ ```
103
166
 
104
167
  return
105
168
 
106
169
  Hello Romain !!
107
170
 
171
+ #### Thot::Varfiles
172
+
173
+
174
+ ##### Synoptic
175
+
176
+ ![synoptic](assets/images/description_thot_varfiles.png)
177
+
178
+ ##### Example
179
+
180
+ ###### File format
181
+
182
+ **Note** : format support is the same for .thot.env files AND given files.
183
+
184
+ Varfiles support both INI and flat format, like :
185
+
186
+ for flat :
187
+
188
+ ```
189
+ key=value
190
+ key = value
191
+ key = value
192
+ # comments and other lines are ignored
193
+ ```
194
+
195
+ for INI :
196
+
197
+ ```
198
+ key=value
199
+ key = value
200
+ key = value
201
+ # comments and other lines are ignored
202
+
203
+ [EVT]
204
+ key=value
205
+ key = value
206
+ key = value
207
+ # comments and other lines are ignored
208
+ ```
209
+
210
+ **Note** : section are overrides of global values
108
211
 
212
+ ###### Sample files for examples
213
+
214
+ ./.thot.env file :
215
+
216
+ ```
217
+ key=first value
218
+ key2=first value
219
+ ```
220
+
221
+
222
+ Given file : "/path/to/myfile.ini"
223
+
224
+ ```
225
+ key=value
226
+
227
+ [development]
228
+ key=dev value
229
+
230
+ [staging]
231
+ key=staging value
232
+
233
+ ```
234
+ ###### For :developement evt, without changing dotfiles preloaded.
235
+
236
+
237
+
238
+ ```ruby
239
+ require 'thot'
240
+ include Thot
241
+ vars = Varfiles::new varfile: "/path/to/myfile.ini"
242
+ pp vars.data
243
+ ```
244
+
245
+ output
246
+
247
+ {key: "dev value", key2: "first value"}
248
+
249
+ ###### For :staging evt, without changing dotfiles preloaded.
250
+
251
+
252
+
253
+ ```ruby
254
+ require 'thot'
255
+ include Thot
256
+ vars = Varfiles::new varfile: "/path/to/myfile.ini", environment: :staging
257
+ pp vars.data
258
+ ```
259
+
260
+ output
261
+
262
+ {key: "staging value", key2: "first value"}
263
+
264
+
265
+ ###### For :staging evt, changing dotfiles.
266
+
267
+ **Note** : default dotfiles priority is : ["~/.thot.env","./.thot.env"]
268
+
269
+
270
+
271
+ ```ruby
272
+ require 'thot'
273
+ include Thot
274
+ vars = Varfiles::new varfile: "/path/to/myfile.ini", environment: :staging, dotfiles: []
275
+ pp vars.data
276
+ ```
277
+
278
+ output
279
+
280
+ {key: "staging value"}
109
281
 
110
282
  ### CLI usage
111
283
 
112
- Thot come with a CLI for templating :
284
+ Thot come with a CLI for templating, you could :
113
285
  - reading from STDIN or list files arguments
114
- - getting values from variables file by argument [MANDATORY] --env-var-file FILENAME
286
+ - getting values from variables file by argument --env-var-file, -f FILENAME or from Thot Varfile, see after
115
287
  - display output on STDOUT
116
288
  - verbose mode on STDERR if -v options.
289
+ - defining environment with --environment, -e ENV (efault environement is :development)
290
+ - debug mode on STDERR if -d options (cumulative with verbose).
291
+
292
+ Note : the Thot CLI look for ~/.thot.env file or ./.thot.env file
293
+
294
+ This file support INI format or flat format, keys in INI format are used for environment override
295
+
296
+
297
+
298
+ Note : CLI work only strict mode false, you could have unused keys in datas and undefined value for tokens (substitute by '').
299
+
300
+ Order for variable priorities :
301
+ - ~/.thot.env
302
+ - ./.thot.env
303
+ - file passed by --env-var-file
117
304
 
118
- Note : CLI work only strict mode false, you could have unused keys in datas.
119
305
 
120
306
  #### Pre-requisites
121
307
 
122
- * a file 'template.txt' with : "Hello %%NAME%% !!"
308
+ * a file 'template.ttl' with : "Hello **%%NAME%%** !!"
123
309
  * a variables file with lines, like :
124
310
  ```
125
311
  key=value
@@ -139,25 +325,19 @@ In the same path
139
325
  #### STDIN from echo
140
326
 
141
327
  ```
142
- $ echo "Hello %%NAME%% !!" |thot -e env.test
328
+ $ echo "Hello %%NAME%% !!" |thot -f env.test
143
329
  ```
144
330
 
145
331
  #### STDIN from input
146
332
 
147
333
  ```
148
- $ thot -e env.test < template.txt
149
- ```
150
-
151
- #### Files list
152
-
153
- ```
154
- $ thot -e env.test template1.txt template2.txt
334
+ $ thot -f env.test < template.ttl
155
335
  ```
156
336
 
157
337
  #### Typical usage
158
338
 
159
339
  ```
160
- $ thot -e env.test < template.txt > output.txt
340
+ $ thot -f env.test < template.ttl > output.txt
161
341
  ```
162
342
 
163
343
  ###
data/Rakefile CHANGED
@@ -32,3 +32,18 @@ namespace :yardoc do
32
32
  end
33
33
  end
34
34
  task :clobber => "yardoc:clobber"
35
+
36
+ desc "Run CVE security audit over bundle"
37
+ task :audit do
38
+ system('bundle audit')
39
+ end
40
+
41
+ desc "Run dead line of code detection"
42
+ task :debride do
43
+ system('debride -w .debride_whitelist .')
44
+ end
45
+
46
+ desc "Run SBOM CycloneDX Xml format file"
47
+ task :sbom do
48
+ system('cyclonedx-ruby -p .')
49
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.2.1
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
- options = {}
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
 
@@ -0,0 +1,11 @@
1
+
2
+ # gems
3
+ require 'inifile'
4
+
5
+
6
+ # internals
7
+ require_relative 'thot/version'
8
+ require_relative 'thot/tokenizer'
9
+ require_relative 'thot/processor'
10
+ require_relative 'thot/template'
11
+ require_relative 'thot/varfiles'
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
- STDERR.puts "Generating output" if @options[:verbose]
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
- STDERR.puts "Environment file given : #{@options[:env_var_file]}" if @options[:verbose]
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
- raise "Environment variables file argument missing, (--env-var-file) "
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 @list_templates_file.empty?
33
- STDERR.puts "Reading content from STDIN" if @options[:verbose]
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
- STDERR.puts "Reading content from file(s) : #{@list_templates_file}" if @options[:verbose]
37
- @list_templates_file.each do |item|
38
- if File::exist? item
39
- @content.concat(File::readlines(item).join)
40
- else
41
- raise "file not found #{item}"
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
- res = {}
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
- require "thot/version"
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,5 @@
1
+ var1 = global
2
+ var2 = global
3
+
4
+ [development]
5
+ var1 = local
@@ -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,12 +22,20 @@ 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_development_dependency 'rake', '~> 12.0'
25
+ spec.add_dependency "carioca", "~> 2.1"
26
+ spec.add_dependency "inifile", "~> 3.0"
27
+
28
+ spec.add_development_dependency 'rake', '~> 13.0'
26
29
  spec.add_development_dependency 'rspec', '~> 3.0'
27
- spec.add_development_dependency 'rubocop', '~> 1.32'
30
+ spec.add_development_dependency 'rubocop', '~> 1.54'
28
31
  spec.add_development_dependency "roodi", "~> 5.0"
29
32
  spec.add_development_dependency 'code_statistics', '~> 0.2.13'
30
33
  spec.add_development_dependency "yard", "~> 0.9.27"
31
34
  spec.add_development_dependency "yard-rspec", "~> 0.1"
32
35
  spec.add_development_dependency'version', '~> 1.1'
36
+
37
+ spec.add_development_dependency "bundle-audit", "~> 0.1.0"
38
+
39
+ spec.add_development_dependency "cyclonedx-ruby", "~> 1.1"
40
+ spec.add_development_dependency "debride", "~> 1.12"
33
41
  end
metadata CHANGED
@@ -1,29 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thot
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Romain GEORGES
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-12 00:00:00.000000000 Z
11
+ date: 2023-07-05 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.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.1'
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
16
44
  requirements:
17
45
  - - "~>"
18
46
  - !ruby/object:Gem::Version
19
- version: '12.0'
47
+ version: '13.0'
20
48
  type: :development
21
49
  prerelease: false
22
50
  version_requirements: !ruby/object:Gem::Requirement
23
51
  requirements:
24
52
  - - "~>"
25
53
  - !ruby/object:Gem::Version
26
- version: '12.0'
54
+ version: '13.0'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: rspec
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +72,14 @@ dependencies:
44
72
  requirements:
45
73
  - - "~>"
46
74
  - !ruby/object:Gem::Version
47
- version: '1.32'
75
+ version: '1.54'
48
76
  type: :development
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
80
  - - "~>"
53
81
  - !ruby/object:Gem::Version
54
- version: '1.32'
82
+ version: '1.54'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: roodi
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +150,48 @@ dependencies:
122
150
  - - "~>"
123
151
  - !ruby/object:Gem::Version
124
152
  version: '1.1'
153
+ - !ruby/object:Gem::Dependency
154
+ name: bundle-audit
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 0.1.0
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 0.1.0
167
+ - !ruby/object:Gem::Dependency
168
+ name: cyclonedx-ruby
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '1.1'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '1.1'
181
+ - !ruby/object:Gem::Dependency
182
+ name: debride
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '1.12'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '1.12'
125
195
  description: the simpliest way to template in Ruby and command
126
196
  email:
127
197
  - gems@ultragreen.net
@@ -130,6 +200,7 @@ executables:
130
200
  extensions: []
131
201
  extra_rdoc_files: []
132
202
  files:
203
+ - ".github/workflows/ruby.yml"
133
204
  - ".gitignore"
134
205
  - ".rspec"
135
206
  - ".rubocop.yml"
@@ -138,17 +209,29 @@ files:
138
209
  - README.md
139
210
  - Rakefile
140
211
  - VERSION
212
+ - assets/images/description_thot.png
213
+ - assets/images/description_thot_template.png
214
+ - assets/images/description_thot_varfiles.png
215
+ - assets/images/logo_thot_full_large.png
216
+ - assets/images/logo_thot_light_large.png
217
+ - assets/images/logo_thot_normal_large.png
141
218
  - bin/console
142
219
  - bin/setup
143
220
  - exe/thot
221
+ - lib/dependencies.rb
144
222
  - lib/thot.rb
145
223
  - lib/thot/cli.rb
224
+ - lib/thot/processor.rb
225
+ - lib/thot/rake/manage.rb
226
+ - lib/thot/rake/tasks/template.task
227
+ - lib/thot/template.rb
228
+ - lib/thot/tokenizer.rb
229
+ - lib/thot/varfiles.rb
146
230
  - lib/thot/version.rb
147
231
  - samples/.env.prod
148
- - samples/template.txt
149
- - samples/template2.txt
232
+ - samples/.thot.env
233
+ - samples/template.ttl
150
234
  - thot.gemspec
151
- - ultragreen_roodi_coding_convention.yml
152
235
  homepage: https://github.com/Ultragreen/thot
153
236
  licenses:
154
237
  - MIT
data/samples/template.txt DELETED
@@ -1 +0,0 @@
1
- Hello %%NAME.capitalize%% %%SURNAME.upcase%% !!
@@ -1 +0,0 @@
1
- Hello %%NAME.capitalize%% %%SURNAME.upcase%% !!
@@ -1,24 +0,0 @@
1
- AssignmentInConditionalCheck:
2
- CaseMissingElseCheck:
3
- ClassLineCountCheck:
4
- line_count: 300 # including comments yard
5
- ClassNameCheck:
6
- pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
7
- #ClassVariableCheck:
8
- CyclomaticComplexityBlockCheck:
9
- complexity: 5
10
- CyclomaticComplexityMethodCheck:
11
- complexity: 10
12
- EmptyRescueBodyCheck:
13
- ForLoopCheck:
14
- MethodLineCountCheck:
15
- line_count: 30
16
- MethodNameCheck:
17
- pattern: !ruby/regexp /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
18
- # MissingForeignKeyIndexCheck:
19
- ModuleLineCountCheck:
20
- line_count: 300
21
- ModuleNameCheck:
22
- pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
23
- ParameterNumberCheck:
24
- parameter_count: 5