tzispa_rig 0.2.3
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 +7 -0
- data/CHANGELOG.md +24 -0
- data/README.md +3 -0
- data/lib/tzispa/rig/binder.rb +104 -0
- data/lib/tzispa/rig/parameters.rb +56 -0
- data/lib/tzispa/rig/parser.rb +165 -0
- data/lib/tzispa/rig/parsernext.rb +419 -0
- data/lib/tzispa/rig/template.rb +165 -0
- data/lib/tzispa/rig/version.rb +10 -0
- data/lib/tzispa/rig.rb +11 -0
- data/lib/tzispa_rig.rb +7 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c00ae9acd7890ced20f0f74e4c54143d458f0907
|
4
|
+
data.tar.gz: 905759eff704cabd933220611ddeaa6b46cc29f4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: defca0f436d1495db948ba55eb11c0ba10fae686ac0f222daaf2abe581b384df0a3c1d40da525c4d0acbf930a306fb4113d2dc33872524b4a98e30df33a0c421
|
7
|
+
data.tar.gz: abee73f6183769d5591560d1e2a5dbf1b0d877b4184771237505cfaa2b9ff14ffe5a7d368d4b2259d480d7bd97c1a1f89bcdaebd389c16a58678c6488011ba5a
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Tzispa Rig
|
2
|
+
|
3
|
+
Rig templates implementation
|
4
|
+
|
5
|
+
## v0.2.3
|
6
|
+
- Fix in the api call regex syntax
|
7
|
+
- Added prefix in api calls
|
8
|
+
|
9
|
+
## v0.2.2
|
10
|
+
- Bug fix in iblk render
|
11
|
+
- Remake of the Binder class defining two specialized classes: TemplateBinder and LoopBinder
|
12
|
+
- Bug fix and improvements in the statements parser
|
13
|
+
- Bug fix in the loop_binder method not allowing 2 loops with equal ids at the same level
|
14
|
+
|
15
|
+
## v0.2.1
|
16
|
+
- Regexp optimizations
|
17
|
+
- Some classes has been renamed for better code readability
|
18
|
+
|
19
|
+
## v0.2.0
|
20
|
+
- Implemented new parser to break away parsing and rendering
|
21
|
+
- Implemented template caching class 'Engine' with on demand parsing: a block template is parsed only when if it's new or if it has been modified
|
22
|
+
|
23
|
+
## v0.1.0
|
24
|
+
- Initial release: code moved from tzispa main gem
|
data/README.md
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'tzispa/rig'
|
5
|
+
|
6
|
+
module Tzispa
|
7
|
+
module Rig
|
8
|
+
|
9
|
+
|
10
|
+
class Binder
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
attr_reader :context, :dataStruct, :parser
|
14
|
+
def_delegators :@parser, :attribute_tags
|
15
|
+
def_delegators :@context, :app, :request, :response
|
16
|
+
|
17
|
+
|
18
|
+
def initialize(parser, context)
|
19
|
+
@parser = parser
|
20
|
+
@context = context
|
21
|
+
@dataStruct = attribute_tags.count > 0 ? Struct.new(*attribute_tags) : Struct.new(nil)
|
22
|
+
end
|
23
|
+
|
24
|
+
def loop_binder(loop_id)
|
25
|
+
loop_parser = @parser.loop_parser loop_id
|
26
|
+
raise ArgumentError.new("#{self.class}:: there isn't any loop tagged '#{loop_id}'") unless loop_parser && loop_parser.count > 0
|
27
|
+
raise ArgumentError.new("#{self.class}:: there are #{loop_parser.count} loops tagged '#{loop_id}' at the same level: only one allowed") unless loop_parser.count == 1
|
28
|
+
LoopBinder.new loop_parser[0], @context
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
class TemplateBinder < Binder
|
35
|
+
extend Forwardable
|
36
|
+
|
37
|
+
attr_reader :template
|
38
|
+
def_delegators :@template, :params
|
39
|
+
|
40
|
+
|
41
|
+
def initialize(template, context)
|
42
|
+
super(template.parser, context)
|
43
|
+
@template = template
|
44
|
+
end
|
45
|
+
|
46
|
+
def data(**params)
|
47
|
+
(@data ||= @dataStruct.new).tap { |d|
|
48
|
+
params.each{ |k,v|
|
49
|
+
d[k] = v
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.for(template, context)
|
55
|
+
if template.is_block?
|
56
|
+
binder_class = template.binder_class
|
57
|
+
binder_class.new( template, context ) if binder_class
|
58
|
+
else
|
59
|
+
self.new(template, context)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
class LoopBinder < Binder
|
67
|
+
|
68
|
+
attr_reader :data
|
69
|
+
|
70
|
+
def bind!(&generator)
|
71
|
+
@source_object = eval "self", generator.binding
|
72
|
+
@data = instance_eval(&generator).to_enum(:each)
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def method_missing(method, *args, &generator)
|
77
|
+
@source_object.send method, *args, &generator
|
78
|
+
end
|
79
|
+
|
80
|
+
def loop_item(**params)
|
81
|
+
(LoopItem.new self).tap { |item|
|
82
|
+
params.each{ |k,v|
|
83
|
+
item.data[k] = v
|
84
|
+
}
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
class LoopItem
|
92
|
+
|
93
|
+
attr_reader :context, :data
|
94
|
+
|
95
|
+
def initialize(binder)
|
96
|
+
@context = binder.context
|
97
|
+
@data = binder.dataStruct.new
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tzispa
|
4
|
+
module Rig
|
5
|
+
class Parameters
|
6
|
+
|
7
|
+
|
8
|
+
attr_reader :inner
|
9
|
+
attr_reader :outer
|
10
|
+
attr_reader :data
|
11
|
+
|
12
|
+
|
13
|
+
def initialize(params=nil, iouter=',', iinner='=')
|
14
|
+
@data = Hash.new
|
15
|
+
@outer = iouter
|
16
|
+
@inner = iinner
|
17
|
+
setData(params) if params
|
18
|
+
end
|
19
|
+
|
20
|
+
def set(key,value)
|
21
|
+
@data[key.to_sym] = value
|
22
|
+
end
|
23
|
+
|
24
|
+
def get(key)
|
25
|
+
@data[key.to_sym]
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](key)
|
29
|
+
@data[key.to_sym]
|
30
|
+
end
|
31
|
+
|
32
|
+
def has?(key)
|
33
|
+
@data.key?(key.to_sym)
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
@data.map { |k,v| "#{k}#{inner}#{v}" }.join(outer)
|
38
|
+
end
|
39
|
+
|
40
|
+
def merge(params)
|
41
|
+
setData(params)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def setData(params)
|
47
|
+
params.split(outer).each do |param|
|
48
|
+
key,value = param.split(inner)
|
49
|
+
@data[key.to_sym] = value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tzispa/rig/engine'
|
4
|
+
require 'tzispa/rig/formatter'
|
5
|
+
require 'tzispa/helpers/security'
|
6
|
+
|
7
|
+
module Tzispa
|
8
|
+
module Rig
|
9
|
+
class Parser
|
10
|
+
|
11
|
+
include Tzispa::Helpers::Security
|
12
|
+
|
13
|
+
EMPTY_STRING = ''.freeze
|
14
|
+
|
15
|
+
attr_reader :text
|
16
|
+
|
17
|
+
RIG_SYNTAX = {
|
18
|
+
:re_flags => /<flags:(\[(\w+=[^,\]]+(,\w+=[^,\]]+)*?)\])\/>/.freeze,
|
19
|
+
:blk => /<(blk):(\w+(?:\.\w+)?)(?:\[(\w+=[^,\]]+(?:,\w+=[^,\]]+)*)\])?\/>/.freeze,
|
20
|
+
:iblk => /<(iblk):(\w+):(\w+(?:\.\w+)?)(?:\[(\w+=[^,\]]+(?:,\w+=[^,\]]+)*)\])?:(\w+(?:\.\w+)?)(?:\[(\w+=[^,\]]+(?:,\w+=[^,\]]+)*)\])?\/>/.freeze,
|
21
|
+
:static => /<(static):(\w+(?:\.\w+)?)(?:\[(\w+=[^,\]]+(?:,\w+=[^,\]]+)*)\])?\/>/.freeze,
|
22
|
+
:re_var => /<var(\[%[A-Z]?[0-9]*[a-z]\])?:(\w+)\/>/.freeze,
|
23
|
+
:re_metavar => /\{%([^%]+?)%\}/.freeze,
|
24
|
+
:re_loop => /<loop:(\w+)>(.*?)<\/loop:\1>/m.freeze,
|
25
|
+
:re_ife => /<ife:(\w+)>(.*?)(<else:\1\/>(.*?))?<\/ife:\1>/m.freeze,
|
26
|
+
:re_purl => /<purl:(\w+)(\[(\w+=[^,\]]+(,\w+=[^,\]]+)*?)\])?\/>/.freeze,
|
27
|
+
:re_api => /<api:(\w+(?:\.\w+)?):(\w+)(?::([^:]+))?(?::([^\/]+))?\/>/.freeze
|
28
|
+
}
|
29
|
+
|
30
|
+
def initialize(engine, binder=nil, text=nil)
|
31
|
+
@engine = engine
|
32
|
+
@binder = binder || engine.binder
|
33
|
+
@context = engine.context
|
34
|
+
@text = text
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse!
|
38
|
+
@text ||= @engine.content
|
39
|
+
@binder.bind! if @binder && @binder.respond_to?( :bind! )
|
40
|
+
if @engine.type == :block
|
41
|
+
parseStatement
|
42
|
+
parseExpression
|
43
|
+
end
|
44
|
+
parseBlock
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def parseExpression
|
52
|
+
parseMetavar
|
53
|
+
parseVar
|
54
|
+
parsePurl
|
55
|
+
parseAPI
|
56
|
+
end
|
57
|
+
|
58
|
+
def parseStatement
|
59
|
+
parseLoop
|
60
|
+
parseIfe
|
61
|
+
end
|
62
|
+
|
63
|
+
def parseFlags
|
64
|
+
@text.gsub!(RIG_SYNTAX[:re_flags]) { |match|
|
65
|
+
flags = Regexp.last_match(1)
|
66
|
+
@engine.flags = flags
|
67
|
+
EMPTY_STRING
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def parseLoop
|
72
|
+
@text.gsub!(RIG_SYNTAX[:re_loop]) { |match|
|
73
|
+
loopid = Regexp.last_match(1)
|
74
|
+
loopbody = Regexp.last_match(2)
|
75
|
+
lenrig = @binder.respond_to?("#{loopid}") ? @binder.send("#{loopid}") : []
|
76
|
+
lenrig.respond_to?(:map) ? lenrig.map { |item| Parser.new(@engine, item, loopbody.dup).parse!.text }.join : String.new.freeze
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def parseIfe
|
81
|
+
@text.gsub!(RIG_SYNTAX[:re_ife]) { |match|
|
82
|
+
ifetest = Regexp.last_match(1)
|
83
|
+
test = @binder.respond_to?("#{ifetest}") ? @binder.send("#{ifetest}") : false
|
84
|
+
ifebody = Regexp.last_match(2)
|
85
|
+
elsebody = Regexp.last_match(4)
|
86
|
+
Parser.new(@engine, @binder, test ? "#{ifebody}" : "#{elsebody}").parse!.text
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def parseMetavar
|
91
|
+
@text.gsub!(RIG_SYNTAX[:re_metavar]) { |match|
|
92
|
+
varid = Regexp.last_match(1)
|
93
|
+
if @context.respond_to?(varid)
|
94
|
+
@context.send(varid)
|
95
|
+
elsif !@context.env.nil? and !@context.env[varid].nil?
|
96
|
+
@context.env[varid]
|
97
|
+
elsif @binder.respond_to?(varid)
|
98
|
+
@binder.send(varid)
|
99
|
+
else
|
100
|
+
Formatter.unknown(varid)
|
101
|
+
end
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
def parseVar
|
106
|
+
@text.gsub!(RIG_SYNTAX[:re_var]) { |match|
|
107
|
+
fmt = Regexp.last_match(1)
|
108
|
+
varid = Regexp.last_match(2)
|
109
|
+
value = @binder.respond_to?(varid) ? @binder.send(varid) : Formatter.unknown(varid)
|
110
|
+
Formatter.rigvar(value, fmt)
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
def parsePurl
|
115
|
+
@text.gsub!(RIG_SYNTAX[:re_purl]) { |match|
|
116
|
+
urlid = Regexp.last_match[1]
|
117
|
+
urlparams = Parameters.new(Regexp.last_match[3])
|
118
|
+
@engine.app.router_path urlid.to_sym, urlparams.data
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
def parseAPI
|
123
|
+
@text.gsub!(RIG_SYNTAX[:re_api]) { |match|
|
124
|
+
handler, verb, predicate, sufix = Regexp.last_match[1], Regexp.last_match[2], Regexp.last_match[3], Regexp.last_match[4]
|
125
|
+
sign = Parser.sign_array [handler, verb, predicate], @engine.app.config.salt
|
126
|
+
@engine.app.router_path :api, {handler: handler, verb: verb, predicate: predicate, sign: sign, sufix: sufix}
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
def parseBlock
|
131
|
+
reBlocks = Regexp.new(RIG_SYNTAX[:re_blk].to_s + '|' + RIG_SYNTAX[:re_iblk].to_s + '|' + RIG_SYNTAX[:re_static].to_s)
|
132
|
+
|
133
|
+
@text.gsub!(reBlocks) {
|
134
|
+
strtype = (Regexp.last_match[2] || '') << (Regexp.last_match[7] || '') << (Regexp.last_match[16] || '')
|
135
|
+
case strtype
|
136
|
+
when 'blk'
|
137
|
+
rigtype = :block
|
138
|
+
rigname = Regexp.last_match[3]
|
139
|
+
rigparams = Regexp.last_match[5]
|
140
|
+
when 'iblk'
|
141
|
+
rigtype = :block
|
142
|
+
rigtest = Regexp.last_match[8]
|
143
|
+
if (@binder.respond_to?("#{rigtest}?") ? @binder.send("#{rigtest}?") : false)
|
144
|
+
rigname = Regexp.last_match[9]
|
145
|
+
rigparams = Regexp.last_match[11]
|
146
|
+
else
|
147
|
+
rigname = Regexp.last_match[12]
|
148
|
+
rigparams = Regexp.last_match[14]
|
149
|
+
end
|
150
|
+
when 'static'
|
151
|
+
rigtype = :static
|
152
|
+
rigname = Regexp.last_match[17]
|
153
|
+
rigparams = Regexp.last_match[19]
|
154
|
+
else
|
155
|
+
raise ArgumentError.new("Unknown Rig type: #{strtype}")
|
156
|
+
end
|
157
|
+
engine = Engine.new(name: rigname, type: rigtype, parent: @engine, params: rigparams)
|
158
|
+
engine.render!
|
159
|
+
engine.text
|
160
|
+
}
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,419 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'tzispa/domain'
|
5
|
+
require 'tzispa/utils/string'
|
6
|
+
|
7
|
+
|
8
|
+
module Tzispa
|
9
|
+
module Rig
|
10
|
+
|
11
|
+
class ParsedEntity
|
12
|
+
extend Forwardable
|
13
|
+
|
14
|
+
STRING_EMPTY = ''.freeze
|
15
|
+
RE_ANCHOR = /(@@\h+@@)/
|
16
|
+
|
17
|
+
attr_reader :type, :parser
|
18
|
+
def_delegators :@parser, :template
|
19
|
+
|
20
|
+
def initialize(parser, type)
|
21
|
+
@parser = parser
|
22
|
+
@type = type
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.instance(parser, type, match)
|
26
|
+
case type
|
27
|
+
when :meta
|
28
|
+
ParsedMeta.new parser, type, match[1]
|
29
|
+
when :var
|
30
|
+
ParsedVar.new parser, type, match[1], match[2]
|
31
|
+
when :purl
|
32
|
+
ParsedUrl.new parser, type, match[1], match[3]
|
33
|
+
when :api
|
34
|
+
ParsedApi.new parser, type, match[1], match[2], match[3], match[4]
|
35
|
+
when :loop
|
36
|
+
ParsedLoop.new parser, type, match[3], match[4]
|
37
|
+
when :ife
|
38
|
+
ParsedIfe.new parser, type, match[7], match[8], match[10]
|
39
|
+
when :blk
|
40
|
+
ParsedBlock.new parser, type, match[3], match[4]
|
41
|
+
when :iblk
|
42
|
+
ParsedIBlock.new parser, type, match[7], match[8], match[9], match[10], match[11]
|
43
|
+
when :static
|
44
|
+
ParsedStatic.new parser, type, match[14], match[15]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def anchor
|
49
|
+
#[[object_id].pack("h*")].pack("m0")
|
50
|
+
@anchor ||= "@@#{"%x" % object_id}@@".freeze
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
class ParsedMeta < ParsedEntity
|
56
|
+
|
57
|
+
attr_reader :id
|
58
|
+
|
59
|
+
def initialize(parser, type, id)
|
60
|
+
super(parser, type)
|
61
|
+
@id = id.to_sym
|
62
|
+
end
|
63
|
+
|
64
|
+
def render(binder)
|
65
|
+
if binder.data.respond_to? @id
|
66
|
+
binder.data.send(@id).to_s
|
67
|
+
else
|
68
|
+
unknown
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def unknown
|
75
|
+
@unknown ||= "#{@id}:unknown!!".freeze
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
class ParsedVar < ParsedEntity
|
82
|
+
|
83
|
+
attr_reader :id
|
84
|
+
|
85
|
+
def initialize(parser, type, format, id)
|
86
|
+
super(parser, type)
|
87
|
+
@format = format
|
88
|
+
@id = id.to_sym
|
89
|
+
end
|
90
|
+
|
91
|
+
def render(binder)
|
92
|
+
binder.data.respond_to?(@id) ? binder.data.send(@id).to_s : unknown
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def unknown
|
98
|
+
@unknown ||= "#{@id}:unknown!!".freeze
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
class ParsedUrl < ParsedEntity
|
105
|
+
|
106
|
+
def initialize(parser, type, path_id, params)
|
107
|
+
super(parser, type)
|
108
|
+
@path_id = path_id.to_sym
|
109
|
+
@params = params
|
110
|
+
end
|
111
|
+
|
112
|
+
def render(binder)
|
113
|
+
b_params = @params.dup.gsub(RE_ANCHOR) { |match|
|
114
|
+
parser.the_parsed.select { |p| p.anchor == match}.first.render(binder)
|
115
|
+
} if @params
|
116
|
+
binder.context.path @path_id, Parameters.new(b_params).data
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
class ParsedApi < ParsedEntity
|
123
|
+
|
124
|
+
attr_reader :handler, :verb, :predicate
|
125
|
+
|
126
|
+
def initialize(parser, type, handler, verb, predicate, sufix)
|
127
|
+
super(parser, type)
|
128
|
+
@handler = handler
|
129
|
+
@verb = verb
|
130
|
+
@predicate = predicate
|
131
|
+
@sufix = sufix
|
132
|
+
end
|
133
|
+
|
134
|
+
def render(binder)
|
135
|
+
b_handler = bind_value @handler.dup, binder
|
136
|
+
b_verb = bind_value @verb.dup, binder
|
137
|
+
b_predicate = bind_value( @predicate.dup, binder ) if @predicate
|
138
|
+
b_sufix = bind_value( @sufix.dup, binder ) if @sufix
|
139
|
+
binder.context.api b_handler, b_verb, b_predicate, b_sufix
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def bind_value(value, binder)
|
145
|
+
value.gsub(RE_ANCHOR) { |match|
|
146
|
+
parser.the_parsed.select { |p| p.anchor == match}.first.render(binder)
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
class ParsedLoop < ParsedEntity
|
154
|
+
extend Forwardable
|
155
|
+
|
156
|
+
attr_reader :id
|
157
|
+
def_delegators :@loop_parser, :attribute_tags, :loop_parser
|
158
|
+
|
159
|
+
def initialize(parser, type, id, body)
|
160
|
+
super(parser, type)
|
161
|
+
@id = id.to_sym
|
162
|
+
@body = body
|
163
|
+
end
|
164
|
+
|
165
|
+
def parse!
|
166
|
+
@loop_parser = ParserNext.new( template, @body ).parse!
|
167
|
+
self
|
168
|
+
end
|
169
|
+
|
170
|
+
def render(binder)
|
171
|
+
String.new.tap { |text|
|
172
|
+
looper = binder.data.send(@id) if binder.data.respond_to?(@id)
|
173
|
+
looper.data.each { |loop_item|
|
174
|
+
text << @loop_parser.render(loop_item) if loop_item
|
175
|
+
} if looper
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
class ParsedIfe < ParsedEntity
|
183
|
+
|
184
|
+
attr_reader :test
|
185
|
+
|
186
|
+
def initialize(parser, type, test, then_body, else_body)
|
187
|
+
super(parser, type)
|
188
|
+
@test = test.to_sym
|
189
|
+
@then_body = then_body
|
190
|
+
@else_body = else_body
|
191
|
+
end
|
192
|
+
|
193
|
+
def parse!
|
194
|
+
@then_parser = ParserNext.new( template, @then_body ).parse!
|
195
|
+
@else_parser = ParserNext.new( template, @else_body ).parse! if @else_body
|
196
|
+
self
|
197
|
+
end
|
198
|
+
|
199
|
+
def attribute_tags
|
200
|
+
@attribute_tags ||= [@test].concat(@then_parser.attribute_tags).concat((@else_parser && @else_parser.attribute_tags) || Array.new).compact.uniq.freeze
|
201
|
+
end
|
202
|
+
|
203
|
+
def loop_parser(id)
|
204
|
+
@then_parser.loop_parser(id).concat((@else_parser && @else_parser.loop_parser(id)) || Array.new).compact.freeze
|
205
|
+
end
|
206
|
+
|
207
|
+
def render(binder)
|
208
|
+
test_eval = binder.data && binder.data.respond_to?(@test) && binder.data.send(@test)
|
209
|
+
ifeparser = test_eval ? @then_parser : @else_parser
|
210
|
+
ifeparser ? ifeparser.render(binder) : STRING_EMPTY
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
class ParsedBlock < ParsedEntity
|
216
|
+
|
217
|
+
attr_reader :params
|
218
|
+
|
219
|
+
def initialize(parser, type, id, params)
|
220
|
+
super(parser, type)
|
221
|
+
@id = id
|
222
|
+
@params = params
|
223
|
+
end
|
224
|
+
|
225
|
+
def parse!
|
226
|
+
@parsed_block = template.engine.block name: @id, parent: template
|
227
|
+
self
|
228
|
+
end
|
229
|
+
|
230
|
+
def render(binder)
|
231
|
+
blk = @parsed_block.dup
|
232
|
+
if @params
|
233
|
+
b_params = @params.dup.gsub(RE_ANCHOR) { |match|
|
234
|
+
parser.the_parsed.select { |p| p.anchor == match}.first.render(binder)
|
235
|
+
}
|
236
|
+
blk.params = b_params
|
237
|
+
end
|
238
|
+
blk.render binder.context
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
|
243
|
+
class ParsedIBlock < ParsedEntity
|
244
|
+
|
245
|
+
attr_reader :id
|
246
|
+
|
247
|
+
def initialize(parser, type, test, id_then, params_then, id_else, params_else)
|
248
|
+
super(parser, type)
|
249
|
+
@id = test
|
250
|
+
@id_then = id_then
|
251
|
+
@params_then = params_then
|
252
|
+
@id_else = id_else
|
253
|
+
@params_else = params_else
|
254
|
+
end
|
255
|
+
|
256
|
+
def parse!
|
257
|
+
@block_then = template.engine.block name: @id_then, parent: template
|
258
|
+
@block_else = template.engine.block name: @id_else, parent: template
|
259
|
+
self
|
260
|
+
end
|
261
|
+
|
262
|
+
def render(binder)
|
263
|
+
if binder.data.respond_to?(@id) && binder.data.send(@id)
|
264
|
+
blk = @block_then.dup
|
265
|
+
if @params_then
|
266
|
+
b_params = @params_then.dup.gsub(RE_ANCHOR) { |match|
|
267
|
+
parser.the_parsed.select { |p| p.anchor == match}.first.render(binder)
|
268
|
+
}
|
269
|
+
blk.params = b_params
|
270
|
+
end
|
271
|
+
else
|
272
|
+
blk = @block_else.dup
|
273
|
+
if @params_else
|
274
|
+
b_params = @params_else.dup.gsub(RE_ANCHOR) { |match|
|
275
|
+
parser.the_parsed.select { |p| p.anchor == match}.first.render(binder)
|
276
|
+
}
|
277
|
+
blk.params = b_params
|
278
|
+
end
|
279
|
+
end
|
280
|
+
blk.render binder.context
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
|
286
|
+
class ParsedStatic < ParsedEntity
|
287
|
+
|
288
|
+
def initialize(parser, type, id, params)
|
289
|
+
super(parser, type)
|
290
|
+
@id = id
|
291
|
+
@params = params
|
292
|
+
end
|
293
|
+
|
294
|
+
def parse!
|
295
|
+
@parsed_static = template.engine.static name: @id, parent: template
|
296
|
+
self
|
297
|
+
end
|
298
|
+
|
299
|
+
def render(binder)
|
300
|
+
blk = @parsed_static.dup
|
301
|
+
if @params
|
302
|
+
b_params = @params.dup.gsub(RE_ANCHOR) { |match|
|
303
|
+
parser.the_parsed.select { |p| p.anchor == match}.first.render(binder)
|
304
|
+
}
|
305
|
+
blk.params = b_params
|
306
|
+
end
|
307
|
+
blk.render binder.context
|
308
|
+
end
|
309
|
+
|
310
|
+
end
|
311
|
+
|
312
|
+
|
313
|
+
class ParserNext
|
314
|
+
|
315
|
+
EMPTY_STRING = ''
|
316
|
+
|
317
|
+
RIG_EMPTY = {
|
318
|
+
:flags => /<flags:(\[(\w+=[^,\]]+(,\w+=[^,\]]+)*?)\])\/>/
|
319
|
+
}.freeze
|
320
|
+
|
321
|
+
RIG_EXPRESSIONS = {
|
322
|
+
:meta => /\{%([^%]+?)%\}/.freeze,
|
323
|
+
:var => /<var(\[%[A-Z]?[0-9]*[a-z]\])?:(\w+)\/>/,
|
324
|
+
:purl => /<purl:(\w+(?:\.\w+)?)(\[(\w+=[^,\]]+(,\w+=[^,\]]+)*?)\])?\/>/,
|
325
|
+
:api => /<api:([^:\.]+(?:\.[^:]+)?):([^:\/]+)(?::([^:\/]+))?(?::([^\/]+))?\/>/
|
326
|
+
}.freeze
|
327
|
+
|
328
|
+
RIG_STATEMENTS = /(<(loop):(\w+)>(.*?)<\/loop:\3>)|(<(ife):(\w+)>(.*?)(<else:\7\/>(.*?))?<\/ife:\7>)/m
|
329
|
+
|
330
|
+
RIG_TEMPLATES = {
|
331
|
+
:blk => /<(blk):(\w+(?:\.\w+)?)(?:\[(\w+=[^,\]]+(?:,\w+=[^,\]]+)*)\])?\/>/,
|
332
|
+
:iblk => /<(iblk):(\w+):(\w+(?:\.\w+)?)(?:\[(\w+=[^,\]]+(?:,\w+=[^,\]]+)*)\])?:(\w+(?:\.\w+)?)(?:\[(\w+=[^,\]]+(?:,\w+=[^,\]]+)*)\])?\/>/,
|
333
|
+
:static => /<(static):(\w+(?:\.\w+)?)(?:\[(\w+=[^,\]]+(?:,\w+=[^,\]]+)*)\])?\/>/
|
334
|
+
}.freeze
|
335
|
+
|
336
|
+
attr_reader :flags, :template, :the_parsed
|
337
|
+
|
338
|
+
def initialize(template, text=nil)
|
339
|
+
@template = template
|
340
|
+
@inner_text = text ? text : template.content
|
341
|
+
@the_parsed = Array.new
|
342
|
+
end
|
343
|
+
|
344
|
+
def parse!
|
345
|
+
@tags = nil
|
346
|
+
parse_flags
|
347
|
+
if @template.is_block?
|
348
|
+
parse_statements
|
349
|
+
parse_expressions
|
350
|
+
end
|
351
|
+
parse_templates
|
352
|
+
self
|
353
|
+
end
|
354
|
+
|
355
|
+
def render(binder, context=nil)
|
356
|
+
@inner_text.dup.tap { |text|
|
357
|
+
@the_parsed.each { |value|
|
358
|
+
text.gsub! value.anchor, value.render(binder)
|
359
|
+
}
|
360
|
+
}.freeze
|
361
|
+
end
|
362
|
+
|
363
|
+
def attribute_tags
|
364
|
+
@attribute_tags ||= @the_parsed.map { |p|
|
365
|
+
p.id.to_sym if p.respond_to? :id
|
366
|
+
}.concat(@the_parsed.map{ |p|
|
367
|
+
p.attribute_tags if p.type==:ife
|
368
|
+
}).compact.flatten.uniq.freeze
|
369
|
+
end
|
370
|
+
|
371
|
+
def loop_parser(id)
|
372
|
+
@the_parsed.select{ |p| p.type==:loop && p.id==id}.concat(
|
373
|
+
@the_parsed.select{ |p| p.type==:ife }.map { |p| p.loop_parser(id) }.flatten.compact
|
374
|
+
)
|
375
|
+
end
|
376
|
+
|
377
|
+
private
|
378
|
+
|
379
|
+
def parse_flags
|
380
|
+
@inner_text.gsub!(RIG_EMPTY[:flags]) { |match|
|
381
|
+
@flags = Regexp.last_match(1)
|
382
|
+
EMPTY_STRING
|
383
|
+
}
|
384
|
+
end
|
385
|
+
|
386
|
+
def parse_expressions
|
387
|
+
RIG_EXPRESSIONS.each_key { |kre|
|
388
|
+
@inner_text.gsub!(RIG_EXPRESSIONS[kre]) { |match|
|
389
|
+
pe = ParsedEntity.instance(self, kre, Regexp.last_match )
|
390
|
+
@the_parsed << pe
|
391
|
+
pe.anchor
|
392
|
+
}
|
393
|
+
}
|
394
|
+
end
|
395
|
+
|
396
|
+
def parse_statements
|
397
|
+
@inner_text.gsub!(RIG_STATEMENTS) { |match|
|
398
|
+
#puts Regexp.last_match.inspect
|
399
|
+
type = (Regexp.last_match[2] || String.new) << (Regexp.last_match[6] || String.new)
|
400
|
+
pe = ParsedEntity.instance(self, type.to_sym, Regexp.last_match )
|
401
|
+
@the_parsed << pe.parse!
|
402
|
+
pe.anchor
|
403
|
+
}
|
404
|
+
end
|
405
|
+
|
406
|
+
def parse_templates
|
407
|
+
reTemplates = Regexp.new RIG_TEMPLATES.values.map{ |re| "(#{re})"}.join('|')
|
408
|
+
@inner_text.gsub!(reTemplates) { |match|
|
409
|
+
type = (Regexp.last_match[2] || String.new) << (Regexp.last_match[6] || String.new) << (Regexp.last_match[13] || String.new)
|
410
|
+
pe = ParsedEntity.instance(self, type.to_sym, Regexp.last_match )
|
411
|
+
@the_parsed << pe.parse!
|
412
|
+
pe.anchor
|
413
|
+
}
|
414
|
+
end
|
415
|
+
|
416
|
+
end
|
417
|
+
|
418
|
+
end
|
419
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tzispa
|
4
|
+
module Rig
|
5
|
+
|
6
|
+
class NotFound < NameError; end
|
7
|
+
|
8
|
+
class ReadError < IOError; end
|
9
|
+
|
10
|
+
class File
|
11
|
+
|
12
|
+
attr_reader :file, :content, :modified
|
13
|
+
|
14
|
+
def initialize(file)
|
15
|
+
@file = file
|
16
|
+
@loaded = false
|
17
|
+
end
|
18
|
+
|
19
|
+
def loaded?
|
20
|
+
@loaded
|
21
|
+
end
|
22
|
+
|
23
|
+
def modified?
|
24
|
+
@modified != File.mtime(@file)
|
25
|
+
end
|
26
|
+
|
27
|
+
def exists?
|
28
|
+
::File.exists?(@file)
|
29
|
+
end
|
30
|
+
|
31
|
+
def load!
|
32
|
+
begin
|
33
|
+
raise NotFound.new("Template file '#{@file}' not found") unless exists?
|
34
|
+
::File.open(@file, 'r:UTF-8') { |f|
|
35
|
+
@content = String.new
|
36
|
+
@modified = f.mtime
|
37
|
+
while line = f.gets
|
38
|
+
@content << line
|
39
|
+
end
|
40
|
+
}
|
41
|
+
@loaded = true
|
42
|
+
rescue Errno::ENOENT
|
43
|
+
raise ReadError.new "Template file '#{@file}' could not be read"
|
44
|
+
end
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
class Template < File
|
52
|
+
extend Forwardable
|
53
|
+
|
54
|
+
BASIC_TYPES = [:layout, :block, :static].freeze
|
55
|
+
DEFAULT_FORMAT = 'htm'.freeze
|
56
|
+
RIG_EXTENSION = 'rig'.freeze
|
57
|
+
|
58
|
+
attr_reader :name, :type, :domain, :format, :params, :parser, :engine, :subdomain
|
59
|
+
def_delegators :@parser, :attribute_tags
|
60
|
+
|
61
|
+
def initialize(name:, type:, domain: nil, parent: nil, format: nil, params: nil, engine: nil)
|
62
|
+
subdom_name = name.downcase.split('.')
|
63
|
+
@subdomain = subdom_name.first if subdom_name.length > 1
|
64
|
+
@name = subdom_name.last
|
65
|
+
@params = Parameters.new(params)
|
66
|
+
send('type=', type)
|
67
|
+
if parent
|
68
|
+
@engine = engine || parent.engine
|
69
|
+
@domain = domain || parent.domain
|
70
|
+
@format = format || parent.format
|
71
|
+
else
|
72
|
+
@engine = engine
|
73
|
+
@domain = domain
|
74
|
+
@format = format || DEFAULT_FORMAT
|
75
|
+
end
|
76
|
+
raise ArgumentError.new('Missing parameter(s): domain and engine must be especified') unless @domain && @engine
|
77
|
+
super "#{@domain.path}/rig/#{@type.to_s.downcase}/#{@subdomain+'/' if @subdomain}#{@name}.#{RIG_EXTENSION}.#{@format}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def parse!
|
81
|
+
@parser = ParserNext.new self
|
82
|
+
@parser.parse!
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
def render(context)
|
87
|
+
parse! unless @parser
|
88
|
+
binder = TemplateBinder.for self, context
|
89
|
+
binder.bind! if binder && binder.respond_to?(:bind!)
|
90
|
+
@parser.render binder, context
|
91
|
+
end
|
92
|
+
|
93
|
+
def is_block?
|
94
|
+
@type == :block
|
95
|
+
end
|
96
|
+
|
97
|
+
def is_layout?
|
98
|
+
@type == :layout
|
99
|
+
end
|
100
|
+
|
101
|
+
def is_static?
|
102
|
+
@type == :static
|
103
|
+
end
|
104
|
+
|
105
|
+
def params=(value)
|
106
|
+
@params = Parameters.new(value)
|
107
|
+
end
|
108
|
+
|
109
|
+
def params
|
110
|
+
@params.data
|
111
|
+
end
|
112
|
+
|
113
|
+
def binder_class
|
114
|
+
@domain.require "rig/#{@type}/#{@subdomain+'/' if @subdomain}#{@name.downcase}"
|
115
|
+
TzString.constantize "#{TzString.camelize @domain.name}::Rig::#{@type.to_s.capitalize}::#{TzString.camelize(@subdomain+'::') if @subdomain}#{TzString.camelize @name }"
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def type=(value)
|
121
|
+
raise ArgumentError.new("#{value} is not a Rig block") unless BASIC_TYPES.include?(value)
|
122
|
+
@type = value
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
class Engine
|
129
|
+
|
130
|
+
attr_reader :app
|
131
|
+
|
132
|
+
def initialize(app)
|
133
|
+
@app = app
|
134
|
+
@pool= Hash.new
|
135
|
+
@mutex = Mutex.new
|
136
|
+
end
|
137
|
+
|
138
|
+
def layout(name:, format:nil, params:nil)
|
139
|
+
rig_template name, :layout, format, params, nil
|
140
|
+
end
|
141
|
+
|
142
|
+
def block(name:, format:nil, params:nil, parent:nil)
|
143
|
+
rig_template name, :block, format, params, parent
|
144
|
+
end
|
145
|
+
|
146
|
+
def static(name:, format:nil, params:nil, parent:nil)
|
147
|
+
rig_template name, :static, format, params, parent
|
148
|
+
end
|
149
|
+
|
150
|
+
def rig_template(name, type, format, params, parent)
|
151
|
+
if @mutex.owned?
|
152
|
+
tpl = @pool["#{type}__#{name}"] || Template.new( name: name, type: type, format: format, domain: @app.domain, params: params, parent: parent, engine: self )
|
153
|
+
tpl.loaded? && !tpl.modified? ? tpl : tpl.load!.parse!
|
154
|
+
else
|
155
|
+
@mutex.synchronize {
|
156
|
+
tpl = @pool["#{type}__#{name}"] || Template.new( name: name, type: type, format: format, domain: @app.domain, params: params, parent: parent, engine: self )
|
157
|
+
tpl.loaded? && !tpl.modified? ? tpl : tpl.load!.parse!
|
158
|
+
}
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
data/lib/tzispa/rig.rb
ADDED
data/lib/tzispa_rig.rb
ADDED
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tzispa_rig
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Juan Antonio Piñero
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-02-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tzispa_helpers
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tzispa_utils
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.1'
|
41
|
+
description: General purpose template engine
|
42
|
+
email:
|
43
|
+
- japinero@area-integral.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- CHANGELOG.md
|
49
|
+
- README.md
|
50
|
+
- lib/tzispa/rig.rb
|
51
|
+
- lib/tzispa/rig/binder.rb
|
52
|
+
- lib/tzispa/rig/parameters.rb
|
53
|
+
- lib/tzispa/rig/parser.rb
|
54
|
+
- lib/tzispa/rig/parsernext.rb
|
55
|
+
- lib/tzispa/rig/template.rb
|
56
|
+
- lib/tzispa/rig/version.rb
|
57
|
+
- lib/tzispa_rig.rb
|
58
|
+
homepage: https://www.area-integral.com
|
59
|
+
licenses:
|
60
|
+
- MIT
|
61
|
+
metadata: {}
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - "~>"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '2.0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.0'
|
76
|
+
requirements: []
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 2.5.1
|
79
|
+
signing_key:
|
80
|
+
specification_version: 4
|
81
|
+
summary: General purpose template engine
|
82
|
+
test_files: []
|