thot 1.1.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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