thot 1.0.1 → 1.2.0

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: 66b39148b6f0d21ab8174c953757815943eda80b28000caba1dbdd7821d12ef2
4
- data.tar.gz: 5a904474b751660d49e77f1c835490b3c945dda97dc49f9d16c427db9664ecdf
3
+ metadata.gz: b8633ef1b1cb491681966a9cb0439d1e3d8bb69766291623757b0f10fb13ed27
4
+ data.tar.gz: b7f7297728de329b92fcb064aeeff5d92249b29d9aeb5c430a0a4cf767a4829c
5
5
  SHA512:
6
- metadata.gz: 42cb44934894d02699bcd17c3ba22c7d32b2b1effe5d4e19acdc1e6c9662c4fb623103742de2f54d375cbfbcac47086d17ffb4ca2b2771ea34cc13159be020f4
7
- data.tar.gz: 77c8256484294a9e089a2b7320bdf02382600ffd62e26b4e8967843d91710ad3a23dca2d4cce7b127e49b2126ce0150544604455aaaea727cc975a3d85dc52dd
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.0.1
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: example.rb [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,42 +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
- if @list_templates_file.empty?
32
- STDERR.puts "Reading content from STDIN" if @options[:verbose]
40
+ @content = ""
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?
33
44
  @content = ARGF.readlines.join
34
45
  else
35
- STDERR.puts "Reading content from file(s) : #{@list_templates_file}" if @options[:verbose]
36
- @list_templates_file.each do |item|
37
- if File::exist? item
38
- @content.concat(File::readlines(item)).join
39
- else
40
- raise "file not found #{item}"
41
- end
42
- 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
43
52
  end
44
53
  end
45
54
 
46
55
  def read_evt_file(file)
47
- res = {}
48
- if File::exist? file
49
- content = File::readlines(file)
50
- else
51
- raise "Environment variables file not found #{file}"
52
- end
53
- content.each do |line|
54
- next if line =~ /#/
55
- key,value = line.split('=')
56
- res[key.strip.to_sym] = value.strip if value
57
- end
58
- return res
56
+ return
59
57
  end
60
58
 
61
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,126 +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
- raise InvalidTokenList::new("Token list malformation") unless _data.keys.sort == @list_token.map{|_token| _token.to_s }.sort
77
- @hash_token = _data
78
- end
79
-
80
- # collector for pseudo accessor to prevent bad mapping
81
- # @raise [NotAToken] if caling an accessor not mapped in token list
82
- def method_missing(_name,*_args)
83
- raise NotAToken
84
- end
85
-
86
- # the templater;proceed to templating
87
- # @return [String] the template output
88
- def output
89
- @result = @content
90
- @list_token.each{|_token|
91
- self.filtering @content.scan(/%%(#{_token.to_s.upcase}[\.\w+]+)%%/).flatten
92
- @result.gsub!(/%%#{_token.to_s.upcase}%%/,@hash_token[_token.to_s])
93
- }
94
- return @result
95
- end
96
-
97
- private
98
-
99
- def filtering(list)
100
- @filtered_tokens = {}
101
- list.each do |pipe|
102
- token, *filters = pipe.split('.')
103
- @filtered_tokens[pipe] = @hash_token[token.downcase]
104
- filters.each do |filter|
105
- @filtered_tokens[pipe] = @filtered_tokens[pipe].send filter.to_sym if @filtered_tokens[pipe].respond_to? filter.to_sym
106
- end
107
- end
108
- @filtered_tokens.each do |item,value|
109
- @result.gsub!(/%%#{item}%%/,value)
110
- end
111
- end
112
-
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
-
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.0.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Romain GEORGES
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-17 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,14 +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
187
+ - samples/.thot.env
188
+ - samples/template.ttl
149
189
  - thot.gemspec
150
190
  - ultragreen_roodi_coding_convention.yml
151
191
  homepage: https://github.com/Ultragreen/thot
@@ -168,7 +208,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
168
208
  - !ruby/object:Gem::Version
169
209
  version: '0'
170
210
  requirements: []
171
- rubygems_version: 3.1.2
211
+ rubygems_version: 3.3.5
172
212
  signing_key:
173
213
  specification_version: 4
174
214
  summary: THe Operative Templating
data/samples/template.txt DELETED
@@ -1 +0,0 @@
1
- Hello %%NAME.capitalize%% %%SURNAME.upcase%% !!