tanuki 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +5 -4
- data/app/tanuki/controller/{link.thtml → controller.link.thtml} +0 -0
- data/app/tanuki/controller/controller.page.thtml +14 -0
- data/app/tanuki/controller/controller.rb +1 -2
- data/app/tanuki/controller/controller.title.ttxt +1 -0
- data/app/tanuki/controller/controller.view.thtml +3 -0
- data/app/tanuki/fetcher/sequel/sequel.rb +34 -0
- data/app/tanuki/manager/controller/controller.rb +1 -1
- data/app/tanuki/manager/page/page.rb +1 -1
- data/app/tanuki/meta_model/{manager.ttxt → meta_model.manager.ttxt} +0 -0
- data/app/tanuki/meta_model/{manager_base.ttxt → meta_model.manager_base.ttxt} +0 -0
- data/app/tanuki/meta_model/{model.ttxt → meta_model.model.ttxt} +0 -0
- data/app/tanuki/meta_model/{model_base.ttxt → meta_model.model_base.ttxt} +0 -0
- data/app/tanuki/meta_model/meta_model.rb +1 -2
- data/app/tanuki/model/controller/controller.rb +1 -1
- data/app/tanuki/model/page/page.rb +1 -1
- data/app/tanuki/page/missing/{default.thtml → missing.page.thtml} +1 -1
- data/app/tanuki/page/missing/missing.rb +3 -2
- data/app/user/page/home/home.rb +2 -0
- data/app/user/page/home/home.title.thtml +1 -0
- data/app/user/page/home/home.view.css +88 -0
- data/app/user/page/home/home.view.thtml +22 -0
- data/bin/tanuki +2 -1
- data/config/common.rb +1 -0
- data/config/common_application.rb +3 -6
- data/config/development_application.rb +0 -3
- data/lib/tanuki.rb +8 -7
- data/lib/tanuki/application.rb +108 -81
- data/lib/tanuki/argument.rb +10 -5
- data/lib/tanuki/argument/integer_range.rb +4 -2
- data/lib/tanuki/{behavior/object_behavior.rb → base_behavior.rb} +21 -4
- data/lib/tanuki/configurator.rb +20 -8
- data/lib/tanuki/const.rb +32 -0
- data/lib/tanuki/context.rb +18 -7
- data/lib/tanuki/controller.rb +517 -0
- data/lib/tanuki/css_compressor.rb +50 -0
- data/lib/tanuki/extensions/module.rb +21 -5
- data/lib/tanuki/extensions/rack/frozen_route.rb +35 -0
- data/lib/tanuki/extensions/rack/static_dir.rb +1 -1
- data/lib/tanuki/extensions/sequel/model.rb +7 -0
- data/lib/tanuki/i18n.rb +8 -6
- data/lib/tanuki/loader.rb +166 -33
- data/lib/tanuki/meta_model.rb +176 -0
- data/lib/tanuki/{behavior/model_behavior.rb → model_behavior.rb} +7 -3
- data/lib/tanuki/model_generator.rb +49 -29
- data/lib/tanuki/template_compiler.rb +72 -41
- data/lib/tanuki/utility.rb +11 -4
- data/lib/tanuki/utility/create.rb +52 -11
- data/lib/tanuki/utility/generate.rb +16 -10
- data/lib/tanuki/utility/version.rb +1 -1
- data/lib/tanuki/version.rb +7 -2
- metadata +50 -66
- data/app/tanuki/controller/default.thtml +0 -5
- data/app/tanuki/controller/index.thtml +0 -1
- data/app/user/page/index/default.thtml +0 -121
- data/app/user/page/index/index.rb +0 -2
- data/config/test_application.rb +0 -2
- data/lib/tanuki/behavior/controller_behavior.rb +0 -366
- data/lib/tanuki/behavior/meta_model_behavior.rb +0 -160
- data/lib/tanuki/extensions/rack/builder.rb +0 -26
- data/lib/tanuki/extensions/rack/server.rb +0 -18
- data/lib/tanuki/launcher.rb +0 -21
- data/lib/tanuki/utility/server.rb +0 -23
@@ -0,0 +1,176 @@
|
|
1
|
+
module Tanuki
|
2
|
+
|
3
|
+
# Tanuki::MetaModel contains all methods for the meta-model.
|
4
|
+
class MetaModel
|
5
|
+
|
6
|
+
include Tanuki::BaseBehavior
|
7
|
+
|
8
|
+
# Creates new meta-model +name+ in +namespace+.
|
9
|
+
# Model schema is passed as +data+.
|
10
|
+
# Stucture +models+ contains all models being generated.
|
11
|
+
def initialize(namespace, name, data, models)
|
12
|
+
@namespace = namespace
|
13
|
+
@name = name
|
14
|
+
@data = data
|
15
|
+
@models = models
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns class name for a given class type.
|
19
|
+
def class_name_for(class_type)
|
20
|
+
case class_type
|
21
|
+
when :model, :model_base then "#{@namespace}::Model::#{@name}"
|
22
|
+
when :manager, :manager_base then "#{@namespace}::Manager::#{@name}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns an array of code for alias-column name pair.
|
27
|
+
def key
|
28
|
+
@key.inspect
|
29
|
+
end
|
30
|
+
|
31
|
+
# Prepares data for template generation.
|
32
|
+
# Processes own keys, fields, etc.
|
33
|
+
def process
|
34
|
+
process_source
|
35
|
+
process_key
|
36
|
+
process_joins
|
37
|
+
process_filter
|
38
|
+
process_order
|
39
|
+
end
|
40
|
+
|
41
|
+
# Extracts the model firts-source information form the YAML @data
|
42
|
+
# and performs
|
43
|
+
def process_source
|
44
|
+
guess_table = @name.pluralize
|
45
|
+
@data ||= {}
|
46
|
+
@source = @data['source'] || guess_table
|
47
|
+
@source = {'table' => @source} if @source.is_a? String
|
48
|
+
@first_source = (@source['table'] || guess_table).downcase.to_sym
|
49
|
+
end
|
50
|
+
|
51
|
+
def process_key
|
52
|
+
if @source.include? 'key' && @source['key'].nil?
|
53
|
+
@key = []
|
54
|
+
else
|
55
|
+
@key = @source['key'] || 'id'
|
56
|
+
end
|
57
|
+
@key = [@key] if @key.is_a? String
|
58
|
+
raise 'invalid key' unless @key.is_a? Array
|
59
|
+
@key.map! do |k|
|
60
|
+
parts = k.split('.').map {|p| p.to_sym }
|
61
|
+
raise "invalid key field #{k}" if parts.count > 2
|
62
|
+
if parts.count == 2
|
63
|
+
if parts[0] != @first_source.to_s
|
64
|
+
raise 'all key fields should belong to the first-source'
|
65
|
+
end
|
66
|
+
parts
|
67
|
+
else
|
68
|
+
[@first_source, parts[0]]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def process_joins
|
74
|
+
@joins = {}
|
75
|
+
@joins[@first_source] = nil
|
76
|
+
joins = @source['joins'] || {}
|
77
|
+
joins = [joins] if joins.is_a? String
|
78
|
+
if joins.is_a? Array
|
79
|
+
joins = Hash[*joins.collect {|v| [v, nil] }.flatten]
|
80
|
+
end
|
81
|
+
if joins.is_a? Hash
|
82
|
+
joins.each_pair do |table_alias, join|
|
83
|
+
table_alias = table_alias.to_sym
|
84
|
+
if @joins.include? table_alias
|
85
|
+
raise "#{table_alias} is already in use"
|
86
|
+
end
|
87
|
+
if join && (join['on'].is_a? Hash)
|
88
|
+
table_name = join['table'] || table_alias
|
89
|
+
on = join['on']
|
90
|
+
join_type = (join['type'] || 'inner').to_sym
|
91
|
+
else
|
92
|
+
on = join
|
93
|
+
table_name = table_alias
|
94
|
+
join_type = :inner
|
95
|
+
end
|
96
|
+
if on
|
97
|
+
on.map! do |lhs, rhs|
|
98
|
+
[
|
99
|
+
[lhs, table_alias],
|
100
|
+
[rhs, @first_source]
|
101
|
+
].map do |side, table_alias|
|
102
|
+
if side.is_a? String
|
103
|
+
if m = side.match(/^\(('|")(.*)\1\)$/)
|
104
|
+
m[2]
|
105
|
+
else
|
106
|
+
parts = side.split('.').map {|x| x.to_sym }
|
107
|
+
case parts.count
|
108
|
+
when 1
|
109
|
+
[table_alias, parts[0]]
|
110
|
+
when 2
|
111
|
+
unless @joins.include? parts[0]
|
112
|
+
raise "unknown alias #{parts[0]}"
|
113
|
+
end
|
114
|
+
parts
|
115
|
+
else
|
116
|
+
raise "invalid column specification #{lhs}"
|
117
|
+
end # case
|
118
|
+
end # if match
|
119
|
+
else
|
120
|
+
side
|
121
|
+
end # if
|
122
|
+
end # map
|
123
|
+
end # map!
|
124
|
+
on = Hash[*on.flatten]
|
125
|
+
else
|
126
|
+
on = {}
|
127
|
+
@key.each do |k|
|
128
|
+
on[[
|
129
|
+
table_alias,
|
130
|
+
(@first_source.to_s.singularize << '_' << k[1].to_s).to_sym
|
131
|
+
]] = k
|
132
|
+
end # each
|
133
|
+
end # if
|
134
|
+
@joins[table_alias] = {
|
135
|
+
:type => join_type,
|
136
|
+
:table => table_name,
|
137
|
+
:alias => table_alias,
|
138
|
+
:on => on
|
139
|
+
}
|
140
|
+
end # each_pair
|
141
|
+
else
|
142
|
+
raise "`joins' should be either nil or string or array or hash"
|
143
|
+
end # if
|
144
|
+
end
|
145
|
+
|
146
|
+
# Prepares data for building a Sequel +where+ clause.
|
147
|
+
def process_filter
|
148
|
+
# TODO
|
149
|
+
end
|
150
|
+
|
151
|
+
# Prepares data for building a Sequel +order+ clause.
|
152
|
+
def process_order
|
153
|
+
# TODO
|
154
|
+
end
|
155
|
+
|
156
|
+
# Prepares data for template generation.
|
157
|
+
# Processes foreign keys, fields, etc.
|
158
|
+
def process_relations
|
159
|
+
# TODO
|
160
|
+
end
|
161
|
+
|
162
|
+
# Returns code for alias-column name pair for field +field_name+.
|
163
|
+
def qualified_name(field_name)
|
164
|
+
parts = field_name.split('.')
|
165
|
+
if parts.length == 1
|
166
|
+
"%w{:#{@first_source} :#{field_name}}"
|
167
|
+
elsif parts.length == 2
|
168
|
+
"%w{:#{parts[0]} :#{parts[1]}}"
|
169
|
+
else
|
170
|
+
raise "field name for model #{@namespace}.#{@name} is invalid"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
end # MetaModel
|
175
|
+
|
176
|
+
end # Tanuki
|
@@ -22,7 +22,9 @@ module Tanuki
|
|
22
22
|
@_errors ||= {}
|
23
23
|
@_original ||= {}
|
24
24
|
begin
|
25
|
-
|
25
|
+
unless @_original.include? attribute
|
26
|
+
@_original[attribute] = self[attribute]
|
27
|
+
end
|
26
28
|
self.class[attribute].set(@_data, value)
|
27
29
|
@_errors.delete(attribute)
|
28
30
|
rescue
|
@@ -79,11 +81,13 @@ module Tanuki
|
|
79
81
|
# Creates new model, or returns existing one.
|
80
82
|
def get(data, ctx, lazy=false) # IDENTITY TRACKING AND LAZY LOADING
|
81
83
|
entity_key = extract_key(data)
|
82
|
-
key = [self, entity_key]
|
84
|
+
key = [self, entity_key]
|
85
|
+
# extract_key is generated ad-hoc by model compiler!
|
83
86
|
if cached = ctx.entity_cache[key]
|
84
87
|
cached
|
85
88
|
else
|
86
|
-
ctx.entity_cache[key] = get(*entity_key)
|
89
|
+
ctx.entity_cache[key] = get(*entity_key)
|
90
|
+
# get is generated ad-hoc by model compiler
|
87
91
|
end
|
88
92
|
end
|
89
93
|
|
@@ -3,14 +3,16 @@ module Tanuki
|
|
3
3
|
# Tanuki::ModelGenerator is used for, well, generating models
|
4
4
|
class ModelGenerator
|
5
5
|
|
6
|
-
# A collection of all model definitions represented as hash of hashes
|
7
|
-
# the first nesting level represents namespaces
|
8
|
-
# and the second one contains named model bodies
|
6
|
+
# A collection of all model definitions represented as hash of hashes
|
7
|
+
# where the first nesting level represents namespaces
|
8
|
+
# and the second one contains named model bodies hash.
|
9
9
|
attr_reader :models
|
10
10
|
|
11
|
-
#
|
12
|
-
# Keys in this hash are full qualified names of models
|
13
|
-
#
|
11
|
+
# Hash for collecting the template render-time info for models.
|
12
|
+
# Keys in this hash are full qualified names of models
|
13
|
+
# (+namespace_model_name+). Base models are marked with +(base)+ suffix.
|
14
|
+
# Values are hashes in the form of
|
15
|
+
# +{:generated => [], :failed => [], :skipped => []}+
|
14
16
|
# where all subentries correspond to template generation statuses.
|
15
17
|
attr_reader :tried
|
16
18
|
|
@@ -24,26 +26,41 @@ module Tanuki
|
|
24
26
|
# Loads all models into memory from a given +schema_root+ and prepares
|
25
27
|
# their own properties to be rendered in class templates.
|
26
28
|
def read_models(schema_root)
|
27
|
-
Dir.entries(schema_root).reject {|path| path =~ /\A
|
28
|
-
|
29
|
+
paths = Dir.entries(schema_root).reject {|path| path =~ /\A\..*\Z/ }
|
30
|
+
paths.each do |namespace_path|
|
31
|
+
namespace_name = namespace_path.camelize
|
29
32
|
namespace = @models[namespace_name] = {}
|
30
|
-
|
31
|
-
|
33
|
+
schema_glob = "#{schema_root}/#{namespace_path}/models/*.yml"
|
34
|
+
p schema_glob
|
35
|
+
Dir.glob(schema_glob) do |file_path|
|
36
|
+
|
37
|
+
# Create meta model for namespace and model name from schema
|
38
|
+
model_name = File.basename(file_path, '.yml').camelize
|
32
39
|
meta_model = namespace[model_name] = Tanuki::MetaModel.new(
|
33
40
|
namespace_name,
|
34
41
|
model_name,
|
35
42
|
YAML.load_file(file_path),
|
36
43
|
@models
|
37
44
|
)
|
38
|
-
meta_model.process
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
meta_model.process
|
46
|
+
namespace_model_name = "#{namespace_name}.#{model_name}"
|
47
|
+
|
48
|
+
# TODO is this necessary?
|
49
|
+
next if @tried.include? namespace_model_name
|
50
|
+
|
51
|
+
# Register namespace and model name
|
52
|
+
@tried[namespace_model_name] = {
|
53
|
+
:generated => [],
|
54
|
+
:failed => [],
|
55
|
+
:skipped => []
|
56
|
+
}
|
57
|
+
@tried["#{namespace_model_name} (base)"] = {
|
58
|
+
:generated => [],
|
59
|
+
:failed => [],
|
60
|
+
:skipped => []
|
61
|
+
}
|
62
|
+
end # glob
|
63
|
+
end # each
|
47
64
|
end
|
48
65
|
|
49
66
|
# Generates all models that were read from a given +schema_root+.
|
@@ -57,19 +74,21 @@ module Tanuki
|
|
57
74
|
# Renders a model class by applying +class_type+ template
|
58
75
|
# to a given +meta_model+. Classes are splitted in two parts:
|
59
76
|
# * user-extendable part (+base=false+), which resides in +ctx.app_root+,
|
60
|
-
# * other part with framework-specific code (+base=true+),
|
77
|
+
# * other part with framework-specific code (+base=true+),
|
78
|
+
# which resides in +ctx.gen_root+.
|
61
79
|
# +namespace_model_name+ is used as a label for error report collection
|
62
80
|
# in Tanuki::ModelGenerator#tried hash.
|
63
81
|
def generate_class(meta_model, namespace_model_name, class_type, base)
|
64
82
|
class_name = meta_model.class_name_for class_type
|
65
83
|
namespace_model_name += ' (base)' if base
|
66
|
-
|
84
|
+
root = base ? @ctx.gen_root : @ctx.app_root
|
85
|
+
path = Tanuki::Loader.class_path(class_name, root)
|
67
86
|
if base || !(File.exists? path)
|
68
87
|
begin
|
69
88
|
dirname = File.dirname(path)
|
70
89
|
FileUtils.mkdir_p dirname unless File.directory? dirname
|
71
|
-
File.open
|
72
|
-
writer = proc {|out| file
|
90
|
+
File.open(path, 'w') do |file|
|
91
|
+
writer = proc {|out| file << out.to_s }
|
73
92
|
Loader.run_template({}, meta_model, class_type).call(writer, @ctx)
|
74
93
|
end
|
75
94
|
@tried[namespace_model_name][:generated] << class_name
|
@@ -88,9 +107,9 @@ module Tanuki
|
|
88
107
|
namespace.each do |model_name, meta_model|
|
89
108
|
namespace_model_name = "#{namespace_name}.#{model_name}"
|
90
109
|
{
|
91
|
-
:model
|
92
|
-
:model_base
|
93
|
-
:manager
|
110
|
+
:model => false,
|
111
|
+
:model_base => true,
|
112
|
+
:manager => false,
|
94
113
|
:manager_base => true
|
95
114
|
}.each do |class_type, base|
|
96
115
|
generate_class meta_model, namespace_model_name, class_type, base
|
@@ -99,12 +118,13 @@ module Tanuki
|
|
99
118
|
end
|
100
119
|
end
|
101
120
|
|
102
|
-
# Iterates over all loaded model definitions, giving them a chance
|
103
|
-
# and make any cross-model assumptions
|
121
|
+
# Iterates over all loaded model definitions, giving them a chance
|
122
|
+
# to meet each other and make any cross-model assumptions
|
123
|
+
# (like names for foreign keys).
|
104
124
|
def process_models
|
105
125
|
@models.each do |namespace_name, namespace |
|
106
126
|
namespace.each do |model_name, meta_model|
|
107
|
-
meta_model.process_relations
|
127
|
+
meta_model.process_relations
|
108
128
|
end
|
109
129
|
end
|
110
130
|
end
|
@@ -8,36 +8,45 @@ module Tanuki
|
|
8
8
|
# The following tags are recognized:
|
9
9
|
#
|
10
10
|
# <% Ruby code -- output to stdout %>
|
11
|
+
# <%~ Ruby code -- output to stdout %>
|
11
12
|
# <%= Ruby expression -- replace with result %>
|
12
13
|
# <%# comment -- ignored -- useful in testing %>
|
13
|
-
# % a line of Ruby code -- treated as <% line %>
|
14
|
-
# %% replaced with % if first thing on a line
|
15
14
|
# <%% or %%> -- replace with <% or %> respectively
|
16
15
|
# <%! Ruby expression -- must return a template %> -- renders a template
|
17
16
|
# <%_visitor Ruby code %> -- see Tanuki::Application::visitor for details
|
18
17
|
# <l10n><en>English text</en> ... -- other localizations </l10n>
|
18
|
+
#
|
19
|
+
# All of these tags, except +l10n+, have a single line syntax:
|
20
|
+
#
|
21
|
+
# % a line of Ruby code -- treated as <% line -%>
|
22
|
+
# %~ a line of Ruby code -- treated as <% line -%>
|
23
|
+
# %= Ruby expression -- treated as <%= line -%>
|
24
|
+
# %# comment -- ignored -- treated as <%# line -%>
|
25
|
+
# %% -- replace with % if first thing on a line
|
26
|
+
# %! Ruby expression that returns a template -- treated as <%! line -%>
|
27
|
+
# %_visitor Ruby code -- treated as <%_visitor line -%>
|
19
28
|
class TemplateCompiler
|
20
29
|
|
21
30
|
class << self
|
22
31
|
|
23
|
-
# Compiles a template from a given +src+ string to +ios+
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
code << TEMPLATE_FOOTER % sym
|
37
|
-
obj.instance_eval code
|
32
|
+
# Compiles a template from a given +src+ string to +ios+
|
33
|
+
# for method +sym+ in class +klass+.
|
34
|
+
# If +development+ is false, then no encoding and class declarations,
|
35
|
+
# as well as runtime template checks are generated.
|
36
|
+
def compile_template(ios, src, klass, sym, development=true)
|
37
|
+
ios << TEMPLATE_HEADERS[:class] % [src.encoding, klass] if development
|
38
|
+
ios << TEMPLATE_HEADERS[:method] % sym
|
39
|
+
ios << TEMPLATE_HEADERS[:dev] % sym if development
|
40
|
+
ios << TEMPLATE_HEADERS[:context] % [klass, sym]
|
41
|
+
compile(ios, parse_wiki(src.chomp), true)
|
42
|
+
ios << TEMPLATE_FOOTERS[:dev] % sym if development
|
43
|
+
ios << TEMPLATE_FOOTERS[:method]
|
44
|
+
ios << TEMPLATE_FOOTERS[:class] if development
|
38
45
|
end
|
39
46
|
|
40
|
-
# Replaces all wiki inserts like
|
47
|
+
# Replaces all wiki inserts like
|
48
|
+
# +[[controller?attribute:link#template]]+
|
49
|
+
# with corresponding code in template tags +<%! %>+.
|
41
50
|
def parse_wiki(s)
|
42
51
|
s.gsub WIKI_SYNTAX do
|
43
52
|
code = '<%! self'
|
@@ -52,12 +61,15 @@ module Tanuki
|
|
52
61
|
end
|
53
62
|
}.join
|
54
63
|
|
55
|
-
|
64
|
+
if $~[:model]
|
65
|
+
attrs = $~[:model].split('.').map {|attr| "[:#{attr}]"}.join
|
66
|
+
code << ".model#{attrs}"
|
67
|
+
end
|
56
68
|
code << ".link_to(:#{$~[:link]})" if $~[:link]
|
57
69
|
|
58
70
|
# Template
|
59
71
|
code << case $~[:template]
|
60
|
-
when '' then '.
|
72
|
+
when '' then '.view'
|
61
73
|
when nil then '.link_view'
|
62
74
|
else ".#{$~[:template]}_view"
|
63
75
|
end
|
@@ -76,7 +88,8 @@ module Tanuki
|
|
76
88
|
begin
|
77
89
|
|
78
90
|
# Find out state for expected pattern
|
79
|
-
|
91
|
+
pattern = expect_pattern(state)
|
92
|
+
if new_index = src[index..-1].index(pattern)
|
80
93
|
new_index += index
|
81
94
|
match = src[index..-1].match(pattern)[0]
|
82
95
|
new_state = next_state(state, match)
|
@@ -105,7 +118,8 @@ module Tanuki
|
|
105
118
|
if new_index
|
106
119
|
unless state != :outer && new_state == :code_skip
|
107
120
|
if new_state == :outer
|
108
|
-
|
121
|
+
code_buf << src[index...new_index]
|
122
|
+
process_code_state(ios, code_buf, state)
|
109
123
|
code_buf = ''
|
110
124
|
end
|
111
125
|
index = new_index + match.length
|
@@ -119,20 +133,32 @@ module Tanuki
|
|
119
133
|
end
|
120
134
|
|
121
135
|
end until new_index.nil?
|
122
|
-
|
136
|
+
|
137
|
+
if ensure_output && !(PRINT_STATES.include? last_state)
|
138
|
+
ios << "\n_.('',ctx)"
|
139
|
+
end
|
123
140
|
last_state
|
124
141
|
end
|
125
142
|
|
126
143
|
private
|
127
144
|
|
128
145
|
# Scanner states that output the evaluated result.
|
129
|
-
PRINT_STATES = [:outer, :code_print]
|
146
|
+
PRINT_STATES = [:outer, :code_print].freeze
|
130
147
|
|
131
148
|
# Template header code. Sent to output before compilation.
|
132
|
-
|
149
|
+
TEMPLATE_HEADERS = {
|
150
|
+
:class => "# encoding: %s\nclass %s\n",
|
151
|
+
:method => "def %s_view(*args,&block)\nproc do|_,ctx|\n",
|
152
|
+
:dev => "if _has_tpl ctx,self.class,:%s\n",
|
153
|
+
:context => %{ctx=_ctx(ctx,"%s#%s")}
|
154
|
+
}.freeze
|
133
155
|
|
134
156
|
# Template footer code. Sent to output after compilation.
|
135
|
-
|
157
|
+
TEMPLATE_FOOTERS = {
|
158
|
+
:dev => "\nelse\n(_run_tpl ctx,self,:%s,*args,&block).(_,ctx)\nend\n",
|
159
|
+
:method => "end\nend\n",
|
160
|
+
:class => "end\n"
|
161
|
+
}.freeze
|
136
162
|
|
137
163
|
# Wiki insert syntax
|
138
164
|
WIKI_SYNTAX = %r{
|
@@ -142,22 +168,23 @@ module Tanuki
|
|
142
168
|
(?::(?<link>[a-z_]+))?
|
143
169
|
(?:\#(?<template>[a-z_]*))?
|
144
170
|
\]\]
|
145
|
-
}x
|
171
|
+
}x.freeze
|
146
172
|
|
147
|
-
# Generates code for Ruby template bits from a given +src+ to +ios+
|
173
|
+
# Generates code for Ruby template bits from a given +src+ to +ios+
|
174
|
+
# for a given +state+.
|
148
175
|
def process_code_state(ios, src, state)
|
149
176
|
src.strip!
|
150
177
|
src.gsub!(/^[ \t]+/, '')
|
151
178
|
case state
|
152
|
-
when
|
179
|
+
when /code_(?:line_)?span/ then
|
153
180
|
ios << "\n#{src}"
|
154
|
-
when
|
181
|
+
when /code_(?:line_)?print/ then
|
155
182
|
ios << "\n_.((#{src}),ctx)"
|
156
|
-
when
|
183
|
+
when /code_(?:line_)?template/ then
|
157
184
|
ios << "\n(#{src}).(_,ctx)"
|
158
|
-
when
|
159
|
-
|
160
|
-
ios << "\n#{
|
185
|
+
when /code_(?:line_)?visitor/
|
186
|
+
m = src.match(/^([^ \(]+)?(\([^\)]*\))?\s*(.*)$/)
|
187
|
+
ios << "\n#{m[1]}_result=(#{m[3]}).(#{m[1]}_visitor#{m[2]},ctx)"
|
161
188
|
when :l10n then
|
162
189
|
localize(ios, src)
|
163
190
|
end
|
@@ -166,9 +193,9 @@ module Tanuki
|
|
166
193
|
# Returns the next expected pattern for a given +state+.
|
167
194
|
def expect_pattern(state)
|
168
195
|
case state
|
169
|
-
when :outer then %r{
|
170
|
-
when
|
171
|
-
when
|
196
|
+
when :outer then %r{(?:^\s*|<)%[~=!_#%]?|<l10n>}
|
197
|
+
when /\Acode_line/ then %r{\n|\Z}
|
198
|
+
when /\Acode_(?:span|print|template|visitor|comment)/ then %r{[-%]?%>}
|
172
199
|
when :l10n then %r{<\/l10n>}
|
173
200
|
end
|
174
201
|
end
|
@@ -178,8 +205,6 @@ module Tanuki
|
|
178
205
|
case state
|
179
206
|
when :outer then
|
180
207
|
case match
|
181
|
-
when /\A\s*%\Z/ then :code_line
|
182
|
-
when /\A\s*%%\Z/ then :code_skip
|
183
208
|
when '<%' then :code_span
|
184
209
|
when '<%=' then :code_print
|
185
210
|
when '<%!' then :code_template
|
@@ -187,9 +212,15 @@ module Tanuki
|
|
187
212
|
when '<%#' then :code_comment
|
188
213
|
when '<%%' then :code_skip
|
189
214
|
when '<l10n>' then :l10n
|
215
|
+
when /\A\s*%~?\Z/ then :code_line_span
|
216
|
+
when /\A\s*%=\Z/ then :code_line_print
|
217
|
+
when /\A\s*%!\Z/ then :code_line_template
|
218
|
+
when /\A\s*%_\Z/ then :code_line_visitor
|
219
|
+
when /\A\s*%#\Z/ then :code_line_comment
|
220
|
+
when /\A\s*%%\Z/ then :code_skip
|
190
221
|
end
|
191
|
-
when
|
192
|
-
when
|
222
|
+
when /\Acode_line/ then :outer
|
223
|
+
when /\Acode_(?:span|print|template|visitor|comment)/ then
|
193
224
|
case match
|
194
225
|
when '%%>' then :code_skip
|
195
226
|
else :outer
|