tzispa_rig 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|