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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57ada5bc80f13298187297034ec1b9c754cdf0cd2313bd5a2674f8bc8ad2f165
4
- data.tar.gz: '0961751bbb5709516c88958ee50fee4e62c0802a9ce7b0d58c72bf1a2d544b1a'
3
+ metadata.gz: b8633ef1b1cb491681966a9cb0439d1e3d8bb69766291623757b0f10fb13ed27
4
+ data.tar.gz: b7f7297728de329b92fcb064aeeff5d92249b29d9aeb5c430a0a4cf767a4829c
5
5
  SHA512:
6
- metadata.gz: bb39588c702fbc6ba92ffb46cc16dff45401f18bd8e82c68e10639f242b0ff571b3119b0ba3be83b83f212dffd6b354cfa1519a13603bba0a32e50e3da1d379f
7
- data.tar.gz: a6533192e004e53ea6a9f456a60f11b72fa4e57ff7c9840a19b7e75f7846fa9a8477560f0655f5d2c1ad5d9f3898e4c05c5a52e23124a3f4b9e808a519117782
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
@@ -2,4 +2,3 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
-
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,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 : %%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
+ ### Simple usecase
30
52
 
31
53
  - with data : <pre>{name: 'Romain'}</pre>
32
- - and template content : "Hello %%NAME%% !"
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 %%FIRSTNAME.capitalize%% %%NAME.upcase%% your nickname is : %%NICKNAME.reverse.capitalize%% !"
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
- 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)
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 already a library for you usage and a CLI.
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 %%NAME !!'
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 %%NAME !!'
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 [MANDATORY] --env-var-file FILENAME
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.txt' with : "Hello %%NAME%% !!"
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 -e env.test
205
+ $ echo "Hello %%NAME%% !!" |thot -f env.test
143
206
  ```
144
207
 
145
208
  #### STDIN from input
146
209
 
147
210
  ```
148
- $ thot -e env.test < template.txt
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 -e env.test < template.txt > output.txt
217
+ $ thot -f env.test < template.ttl > output.txt
161
218
  ```
162
219
 
163
220
  ###
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.2.0
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,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.1.0
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: 2022-12-12 00:00:00.000000000 Z
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/template.txt
149
- - samples/template2.txt
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%% !!
@@ -1 +0,0 @@
1
- Hello %%NAME.capitalize%% %%SURNAME.upcase%% !!