lux-fw 0.5.36 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/.version +1 -1
  3. data/bin/README.md +26 -13
  4. data/bin/cli/benchmark.rb +18 -0
  5. data/bin/cli/cerb.rb +90 -0
  6. data/bin/cli/config.rb +4 -11
  7. data/bin/cli/console.rb +72 -30
  8. data/bin/cli/eval.rb +5 -27
  9. data/bin/cli/generate.rb +9 -11
  10. data/bin/cli/get.rb +14 -6
  11. data/bin/cli/memory.rb +12 -0
  12. data/bin/cli/new.rb +2 -0
  13. data/bin/cli/secrets.rb +3 -31
  14. data/bin/cli/server.rb +28 -9
  15. data/bin/cli/stats.rb +26 -20
  16. data/bin/cli/template.rb +10 -0
  17. data/bin/generate_doc +168 -0
  18. data/bin/lux +73 -10
  19. data/lib/README.md +18 -8
  20. data/lib/common/crypt.rb +60 -9
  21. data/lib/common/string_base.rb +54 -15
  22. data/lib/common/struct_opts.rb +30 -0
  23. data/lib/common/time_difference.rb +75 -0
  24. data/lib/loader.rb +41 -0
  25. data/lib/lux/application/README.md +67 -105
  26. data/lib/lux/application/application.rb +87 -329
  27. data/lib/lux/application/lib/magic_routes.rb +22 -0
  28. data/lib/lux/application/lib/nav.rb +218 -99
  29. data/lib/lux/application/lib/render.rb +47 -67
  30. data/lib/lux/application/lib/routes.rb +242 -0
  31. data/lib/lux/application/lib/shared.rb +47 -0
  32. data/lib/lux/application/lux_adapter.rb +6 -0
  33. data/lib/lux/application/lux_config.rb +26 -0
  34. data/lib/lux/cache/Find Results +24 -0
  35. data/lib/lux/cache/README.md +28 -35
  36. data/lib/lux/cache/cache.rb +78 -26
  37. data/lib/lux/cache/lib/memcached_server.rb +36 -0
  38. data/lib/lux/cache/lib/memory_server.rb +44 -0
  39. data/lib/lux/cache/lib/null_server.rb +33 -0
  40. data/lib/lux/cache/lib/sqlite_server.rb +62 -0
  41. data/lib/lux/cache/lux_adapter.rb +19 -0
  42. data/lib/lux/config/config.rb +93 -144
  43. data/lib/lux/config/lux_adapter.rb +27 -0
  44. data/lib/lux/controller/README.md +67 -12
  45. data/lib/lux/controller/controller.rb +237 -187
  46. data/lib/lux/current/README.md +29 -10
  47. data/lib/lux/current/current.rb +166 -77
  48. data/lib/lux/current/lib/current.rb +6 -0
  49. data/lib/lux/current/lib/encrypt_params.rb +38 -36
  50. data/lib/lux/current/lib/session.rb +86 -65
  51. data/lib/lux/current/lux_adapter.rb +5 -0
  52. data/lib/lux/environment/README.md +24 -0
  53. data/lib/lux/environment/environment.rb +91 -0
  54. data/lib/lux/environment/lux_adapter.rb +13 -0
  55. data/lib/lux/error/README.md +18 -19
  56. data/lib/lux/error/error.rb +207 -152
  57. data/lib/lux/error/lux_adapter.rb +9 -0
  58. data/lib/lux/logger/README.md +45 -0
  59. data/lib/lux/logger/lux_adapter.rb +27 -0
  60. data/lib/lux/lux.rb +72 -129
  61. data/lib/lux/mailer/README.md +12 -5
  62. data/lib/lux/mailer/lux_config.rb +4 -0
  63. data/lib/lux/mailer/mailer.rb +81 -59
  64. data/lib/lux/plugin/README.md +19 -0
  65. data/lib/lux/plugin/lux_adapter.rb +8 -0
  66. data/lib/lux/plugin/plugin.rb +65 -0
  67. data/lib/lux/render/README.md +100 -0
  68. data/lib/lux/render/render.rb +12 -0
  69. data/lib/lux/response/README.md +57 -1
  70. data/lib/lux/response/lib/file.rb +87 -65
  71. data/lib/lux/response/lib/flash.rb +49 -34
  72. data/lib/lux/response/lib/header.rb +34 -33
  73. data/lib/lux/response/response.rb +252 -164
  74. data/lib/lux/template/helper.rb +130 -0
  75. data/lib/lux/template/inline_render_proxy.rb +31 -0
  76. data/lib/lux/template/lux_adapter.rb +5 -0
  77. data/lib/lux/template/template.rb +127 -0
  78. data/lib/lux-fw.rb +15 -33
  79. data/lib/overload/array.rb +33 -7
  80. data/lib/overload/blank.rb +8 -3
  81. data/lib/overload/boolean.rb +46 -0
  82. data/lib/overload/class.rb +28 -0
  83. data/lib/overload/dir.rb +117 -7
  84. data/lib/overload/file.rb +40 -5
  85. data/lib/overload/float.rb +38 -3
  86. data/lib/overload/hash.rb +81 -91
  87. data/lib/overload/integer.rb +15 -0
  88. data/lib/overload/json.rb +26 -0
  89. data/lib/overload/nil.rb +4 -8
  90. data/lib/overload/object.rb +91 -24
  91. data/lib/overload/pathname.rb +11 -0
  92. data/lib/overload/raise_variants.rb +26 -36
  93. data/lib/overload/string.rb +100 -27
  94. data/lib/overload/thread_simple.rb +100 -0
  95. data/lib/overload/time.rb +60 -38
  96. data/tasks/loader.rb +20 -7
  97. metadata +170 -169
  98. data/bin/.DS_Store +0 -0
  99. data/bin/build_gem +0 -77
  100. data/bin/cli/dbconsole.rb +0 -8
  101. data/bin/cli/routes.rb +0 -90
  102. data/lib/.DS_Store +0 -0
  103. data/lib/common/.DS_Store +0 -0
  104. data/lib/common/class_attributes.rb +0 -51
  105. data/lib/common/class_callbacks.rb +0 -47
  106. data/lib/common/free_struct.rb +0 -42
  107. data/lib/common/hash_with_indifferent_access.rb +0 -74
  108. data/lib/common/html_tag_builder.rb +0 -91
  109. data/lib/common/method_attr.rb +0 -69
  110. data/lib/common/url.rb +0 -191
  111. data/lib/lux/.DS_Store +0 -0
  112. data/lib/lux/README.md +0 -10
  113. data/lib/lux/cache/.DS_Store +0 -0
  114. data/lib/lux/cache/lib/memory.rb +0 -36
  115. data/lib/lux/cache/lib/null.rb +0 -21
  116. data/lib/lux/config/README.md +0 -63
  117. data/lib/lux/config/lib/plugin.rb +0 -65
  118. data/lib/lux/config/lib/secrets.rb +0 -48
  119. data/lib/lux/current/.DS_Store +0 -0
  120. data/lib/lux/delayed_job/.DS_Store +0 -0
  121. data/lib/lux/delayed_job/README.md +0 -3
  122. data/lib/lux/delayed_job/delayed_job.rb +0 -51
  123. data/lib/lux/delayed_job/lib/memory.rb +0 -16
  124. data/lib/lux/delayed_job/lib/nsq.rb +0 -3
  125. data/lib/lux/delayed_job/lib/postgre.rb +0 -6
  126. data/lib/lux/delayed_job/lib/redis.rb +0 -19
  127. data/lib/lux/event_bus/README.md +0 -36
  128. data/lib/lux/event_bus/event_bus.rb +0 -27
  129. data/lib/lux/view/README.md +0 -85
  130. data/lib/lux/view/cell.rb +0 -102
  131. data/lib/lux/view/helper.rb +0 -120
  132. data/lib/lux/view/lib/cell_helpers.rb +0 -29
  133. data/lib/lux/view/view.rb +0 -95
  134. data/lib/overload/it.rb +0 -29
  135. data/plugins/api/README.md +0 -49
  136. data/plugins/api/api.rb +0 -153
  137. data/plugins/api/lib/attr.rb +0 -31
  138. data/plugins/api/lib/dsl.rb +0 -52
  139. data/plugins/api/lib/error.rb +0 -3
  140. data/plugins/api/lib/model_api.rb +0 -203
  141. data/plugins/api/lib/response.rb +0 -85
  142. data/plugins/db/.DS_Store +0 -0
  143. data/plugins/db/README.md +0 -29
  144. data/plugins/db/auto_migrate/auto_migrate.rb +0 -268
  145. data/plugins/db/auto_migrate/db.rake +0 -15
  146. data/plugins/db/helpers/array_search.rb +0 -27
  147. data/plugins/db/helpers/before_save_filters.rb +0 -32
  148. data/plugins/db/helpers/composite_primary_keys.rb +0 -36
  149. data/plugins/db/helpers/core.rb +0 -94
  150. data/plugins/db/helpers/dataset_methods.rb +0 -138
  151. data/plugins/db/helpers/enums_plugin.rb +0 -52
  152. data/plugins/db/helpers/find_precache.rb +0 -31
  153. data/plugins/db/helpers/link_objects.rb +0 -84
  154. data/plugins/db/helpers/schema_checks.rb +0 -83
  155. data/plugins/db/helpers/typero_adapter.rb +0 -71
  156. data/plugins/db/logger/config.rb +0 -22
  157. data/plugins/db/logger/lux_response_adapter.rb +0 -10
  158. data/plugins/db/paginate/helper.rb +0 -32
  159. data/plugins/db/paginate/sequel_adapter.rb +0 -23
  160. data/plugins/exceptions/exceptions.rake +0 -43
  161. data/plugins/exceptions/simple_exception.rb +0 -64
  162. data/plugins/favicon/favicon.rb +0 -10
  163. data/plugins/html/README.md +0 -3
  164. data/plugins/html/html_form.rb +0 -118
  165. data/plugins/html/html_input.rb +0 -98
  166. data/plugins/html/html_menu.rb +0 -79
  167. data/plugins/html/input_types.rb +0 -346
  168. data/plugins/js_widgets/README.md +0 -5
  169. data/plugins/js_widgets/js/html_tag.coffee +0 -42
  170. data/plugins/js_widgets/js/widgets.coffee +0 -161
  171. data/plugins/js_widgets/js_widgets.rb +0 -15
  172. data/plugins/oauth/lib/facebook.rb +0 -35
  173. data/plugins/oauth/lib/github.rb +0 -38
  174. data/plugins/oauth/lib/google.rb +0 -41
  175. data/plugins/oauth/lib/linkedin.rb +0 -41
  176. data/plugins/oauth/lib/stackexchange.rb +0 -41
  177. data/plugins/oauth/lib/twitter.rb +0 -38
  178. data/plugins/oauth/oauth.rb +0 -42
  179. data/plugins/policy/policy.rb +0 -53
  180. data/tasks/nginx.rake +0 -23
  181. /data/lib/lux/{view/lib → mailer}/helper_modules.rb +0 -0
@@ -1,170 +1,119 @@
1
- # frozen_string_literal: true
2
-
3
- # $LOADED_FEATURES.select{ |f| f.index('/app/') || f.index('/lux/') }
4
-
5
1
  require 'yaml'
2
+ require 'deep_merge'
6
3
 
7
- module Lux::Config
8
- extend self
9
-
10
- # requires all files recrusive in, with spart sort
11
- def require_all dir_path
12
- dir_path = dir_path.to_s.sub(/\/$/,'')
13
- raise '* is not allowed' if dir_path.include?('*')
14
-
15
- glob = `echo #{dir_path}/* #{dir_path}/*/* #{dir_path}/*/*/* #{dir_path}/*/*/*/* #{dir_path}/*/*/*/*/* #{dir_path}/*/*/*/*/*/* |tr ' ' '\n' | grep .rb`.split("\n")
16
- glob.select{ |o| o.index('.rb') }.each do |ruby_file|
17
- require ruby_file
18
- end
19
- end
4
+ module Lux
5
+ module Config
6
+ extend self
20
7
 
21
- # preview config in development
22
- def show_config
23
- for k,v in Lux.config
24
- next if v.kind_of?(Hash)
25
- puts "* config :#{k} = #{v.kind_of?(Hash) ? '{...}' : v}"
8
+ def ram
9
+ `ps -o rss -p #{$$}`.chomp.split("\n").last.to_i / 1000
26
10
  end
27
- end
28
-
29
- def live_require_check!
30
- $live_require_check ||= Time.now
31
11
 
32
- changed_files = $LOADED_FEATURES
33
- .select{ |f| f.include?('/app/') || f.include?('lux') }
34
- .select {|f| File.mtime(f) > $live_require_check }
35
-
36
- for file in changed_files
37
- Lux.log ' Reloaded: %s' % file.split(Lux.root.to_s).last.red
38
- load file
12
+ def start_info
13
+ @load_info ||= proc do
14
+ info = []
15
+
16
+ config = []
17
+ %w(no_cache reload_code show_errors screen_log).each do |name|
18
+ value = Lux.env.send("#{name}?")
19
+ config.push value ? "#{name} (yes)".yellow : "#{name} (no)".green
20
+ end
21
+ info.push "Lux env: #{config.join(', ')}"
22
+
23
+ if $lux_start_time.class == Array
24
+ # $lux_start_time ||= Time.now added to Gemfile
25
+ speed = 'in %s sec (%s gems, %s app)' % [
26
+ time_diff($lux_start_time[0]).white,
27
+ time_diff($lux_start_time[0], $lux_start_time[1]),
28
+ time_diff($lux_start_time[1]),
29
+ ]
30
+ else
31
+ speed = 'in %s sec' % time_diff($lux_start_time).white
32
+ end
33
+
34
+ info.push "* Lux loaded in #{ENV['RACK_ENV']} mode, #{speed}, uses #{ram.to_s.white} MB RAM with total of #{Gem.loaded_specs.keys.length.to_s.white} gems in spec"
35
+ info.join($/)
36
+ end.call
39
37
  end
40
38
 
41
- $live_require_check = Time.now
42
- end
39
+ def set_defaults
40
+ ENV['LUX_ENV'] ||= ''
41
+ ENV['TZ'] ||= 'UTC'
43
42
 
44
- def ram
45
- `ps -o rss -p #{$$}`.chomp.split("\n").last.to_i / 1000
46
- end
43
+ # Delay
44
+ Lux.config.delay_timeout = Lux.env.dev? ? 3600 : 30
47
45
 
48
- def start!
49
- Object.class_callback :config, Lux::Application
50
- start_info $lux_start_time
51
- end
46
+ # Logger
47
+ Lux.config.logger_path_mask = './log/%s.log'
48
+ Lux.config.logger_files_to_keep = 3
49
+ Lux.config.logger_file_max_size = 10_240_000
50
+ Lux.config.logger_formatter = nil
52
51
 
53
- def start_info start=nil
54
- return @load_info if @load_info
52
+ # Other
53
+ Lux.config.use_autoroutes = false
54
+ Lux.config.asset_root = false
55
+ Lux.config[:plugins] ||= []
56
+ Lux.config[:error_logger] ||= Proc.new do |error|
57
+ ap [error.message, error.class, Lux::Error.mark_backtrace(error)]
58
+ end
55
59
 
56
- production_mode = true
57
- production_opts = [
58
- [:compile_assets, false],
59
- [:auto_code_reload, false],
60
- [:dump_errors, false],
61
- [:log_to_stdout, false],
62
- ]
60
+ ###
63
61
 
64
- opts = production_opts.map do |key, production_value|
65
- config_test = Lux.config(key)
66
- config_ok = production_value == config_test
67
- production_mode = false unless config_ok
62
+ # Serve static files is on by default
63
+ Lux.config.serve_static_files = true
68
64
 
69
- data = "#{key} (%s)" % [config_test ? :yes : :no]
70
- config_ok ? data : data.yellow
65
+ # Etag and cache tags reset after deploy
66
+ Lux.config.deploy_timestamp = File.mtime('./Gemfile').to_i.to_s
71
67
  end
72
68
 
73
- mode = production_mode ? 'production'.green : 'development'.yellow
74
- speed =
75
- if start
76
- text = ((Time.now - start)*1000).round.to_s.sub(/(\d)(\d{3})$/,'\1s \2')
77
- ' in %s ms' % text.to_s.white
78
- else
69
+ def app_timeout
70
+ @app_timeout ||= Lux.current.try('[]', :app_timeout) || Lux.config[:app_timeout] || (Lux.env.dev? ? 3600 : 30)
71
+ rescue
72
+ 30
79
73
  end
80
74
 
81
- info = []
82
- info.push '* Config: %s' % opts.join(', ')
83
- info.push "* Lux loaded #{mode} mode#{speed}, uses #{ram.to_s.white} MB RAM with total of #{Gem.loaded_specs.keys.length.to_s.white} gems in spec"
84
-
85
- @@load_info = info.join($/)
86
-
87
- puts @@load_info if start
88
- end
89
-
90
- def init!
91
- # Show server errors to a client
92
- Lux.config.dump_errors = Lux.dev?
93
-
94
- # Log debug output to stdout
95
- Lux.config.log_to_stdout = Lux.dev?
96
-
97
- # Automatic code reloads in development
98
- Lux.config.auto_code_reload = Lux.dev?
99
-
100
- # Runtime compile js and css assets
101
- Lux.config.compile_assets = Lux.dev?
102
-
103
- Lux.config.session_cookie_domain = false
104
- Lux.config.asset_root = false
105
-
106
- ###
107
-
108
- if ENV['LUX_MODE'].to_s.downcase == 'log'
109
- Lux.config.dump_errors = false
110
- Lux.config.auto_code_reload = false
111
- Lux.config.compile_assets = false
75
+ # './config/secrets.yaml'
76
+ # default:
77
+ # development:
78
+ # foo:
79
+ # production:
80
+ # foo:
81
+ def load
82
+ source = Pathname.new './config/config.yaml'
83
+
84
+ if source.exist?
85
+ data = YAML.safe_load source.read, aliases: true
86
+ base = data['default'] || data['base']
87
+
88
+ if base
89
+ base.deep_merge!(data[Lux.env.to_s] || {})
90
+ base['production'] = data['production']
91
+ base
92
+ else
93
+ raise "Secrets :default root not defined in %s" % source
94
+ end
95
+ else
96
+ puts Lux.info '%s not found' % source
97
+ {}
98
+ end
112
99
  end
113
100
 
114
- ###
101
+ private
115
102
 
116
- # Default error logging
117
- Lux.config.error_logger = proc do |error|
118
- ap [error.class, error.message, Lux.error.split_backtrace(error)]
119
-
120
- 'no-key'
103
+ def time_diff time1, time2 = Time.now
104
+ ((time2 - time1)).round(2).to_s
121
105
  end
122
106
 
123
- # Default mail logging
124
- Lux.config.on_mail = proc do |mail|
125
- Lux.logger(:email).info "[#{self.class}.#{@_template} to #{mail.to}] #{mail.subject}"
126
- end
107
+ def env_value_of key, default = :_undef
108
+ value = ENV["LUX_#{key.to_s.upcase}"].to_s
109
+ value = true if ['true', 't', 'yes'].include?(value)
110
+ value = false if ['false', 'f', 'no'].include?(value)
127
111
 
128
- # default event bus error handle
129
- Lux.config.on_event_bus_error = proc do |error, name|
130
- Lux.logger(:event_bus).error '[%s] %s' % [name, error.message]
131
- end
132
-
133
- # server static files
134
- Lux.config.serve_static_files = true
135
-
136
- # Template to show when displaying unhandled server side errors
137
- Lux.config.server_error_template = proc do |text|
138
- text = text.to_s.gsub('<', '&lt;')
139
- text = text.to_s.gsub($/,'<br />')
140
-
141
- %[<html>
142
- <head>
143
- <title>Server error (#{Lux.current.response.status})</title>
144
- </head>
145
- <body style="background:#fff; font-size:12pt; font-family: Arial; padding: 20px;">
146
- <h3>HTTP error #{Lux.current.response.status} in #{Lux.config.app.name}</h3>
147
- <pre style="color:red; padding:10px; background-color: #eee; border: 1px solid #ccc; font-family:'Lucida console'; line-height: 15pt;">#{text}</pre>
148
- <br>
149
- <a href="https://httpstatuses.com/#{Lux.current.response.status}" target="http_error">more info on http error</a>
150
- </body>
151
- </html>]
152
- end
153
-
154
- # inflector
155
- String.inflections do |inflect|
156
- inflect.plural 'bonus', 'bonuses'
157
- inflect.plural 'clothing', 'clothes'
158
- inflect.plural 'people', 'people'
159
- inflect.singular /news$/, 'news'
112
+ if default == :_undef
113
+ value
114
+ else
115
+ value.nil? ? deafult : value
116
+ end
160
117
  end
161
118
  end
162
119
  end
163
-
164
- if Lux.cli?
165
- class Object
166
- def reload!
167
- Lux::Config.live_require_check!
168
- end
169
- end
170
- end
@@ -0,0 +1,27 @@
1
+ module Lux
2
+ # get config hash pointer or die if key provided and not found
3
+ def config
4
+ @lux_config ||= Lux::Config.load.to_hwia
5
+ end
6
+ alias :secrets :config
7
+
8
+ # load rake tasks + including ones in plugins
9
+ def load_tasks
10
+ name = ARGV.first.to_s
11
+
12
+ if name.end_with?(':')
13
+ data = `rake #{name}info 2>&1`
14
+
15
+ unless data.include?('rake aborted!')
16
+ puts "rake #{name}".gray
17
+ puts data
18
+ puts '---'
19
+ end
20
+
21
+ run 'rake -T | grep --color=never %s' % ARGV.first
22
+ exit
23
+ end
24
+
25
+ require_relative '../../../tasks/loader'
26
+ end
27
+ end
@@ -1,18 +1,12 @@
1
- ## Lux::Controller - Simplified Rails like view controllers
1
+ ## Lux::Controller
2
2
 
3
- Controllers are Lux view models
3
+ Similar to Rails Controllers
4
4
 
5
- * all cells shoud inherit from Lux::Controller
6
- * `before`, `before_action` and `after` class methods supportd
7
- * instance_method `on_error` is supported
5
+ * `before`, `before_action`, `after` and `rescue_from` class methods supportd
8
6
  * calls templates as default action, behaves as Rails controller.
9
7
 
10
- ### Example code
11
-
12
8
  ```ruby
13
- require 'lux-fw'
14
-
15
- class Main::RootController < Lux::Controller
9
+ class RootController < ApplicationController
16
10
  # action to perform before
17
11
  before do
18
12
  @org = Org.find @org_id if @org_id
@@ -25,6 +19,8 @@ class Main::RootController < Lux::Controller
25
19
  # ...
26
20
  end
27
21
 
22
+ template_location './app/views' # default
23
+
28
24
  ###
29
25
 
30
26
  mock :show # mock `show` action
@@ -34,7 +30,7 @@ class Main::RootController < Lux::Controller
34
30
  end
35
31
 
36
32
  def foo
37
- # renders ./app/views/main/root/foo.(haml, erb)
33
+ # renders ./app/views/root/foo.(haml, erb)
38
34
  end
39
35
 
40
36
  def baz
@@ -45,5 +41,64 @@ class Main::RootController < Lux::Controller
45
41
  render json: { data: 'Bar text' }
46
42
  end
47
43
 
44
+ def transfer
45
+ # transfer to :baz
46
+ action :baz
47
+
48
+ # transfer to Another::Foo#bar
49
+ action 'another/foo#bar'
50
+ end
48
51
  end
49
- ```
52
+ ```
53
+
54
+ Render method can accept numerous parameters
55
+
56
+ ```ruby
57
+ class MainController
58
+ def foo
59
+ render text: 'foo'
60
+ render plain: 'foo'
61
+ render html: '<html>...'
62
+ render json: {}
63
+ render javascript: '...'
64
+ render template: false, content_type: :text
65
+ render template: './some/template.haml', data: @template_data
66
+
67
+ # helpers
68
+ helper.link_to # MainHelper.link_to
69
+ helper(:bar) # BarHelper.link_to
70
+
71
+ # respond to formats
72
+ respond_to :js do ...
73
+ respond_to do |format|
74
+ case format
75
+ when nil # /foo
76
+ # ...
77
+ when :js # /foo.js
78
+ # ...
79
+ end
80
+ end
81
+ ```
82
+
83
+ Definable callbacks
84
+
85
+ ```ruby
86
+ before do ... # before all
87
+ before_action do ... # before action
88
+ before_render do ... # before render
89
+ after do ... # after all
90
+ ```
91
+
92
+ Definable class variables
93
+
94
+ ```ruby
95
+ # define master layout
96
+ # string is template, symbol is method pointer and lambda is lambda
97
+ layout './some/layout.haml'
98
+
99
+ # define helper contest, by defult derived from class name
100
+ helper :global
101
+
102
+ # custom template root instead calcualted one
103
+ template_root './apps/admin/views'
104
+ ```