ramaze 0.1.1 → 0.1.2
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/Rakefile +14 -29
- data/bin/ramaze +2 -3
- data/doc/AUTHORS +5 -2
- data/doc/CHANGELOG +262 -9
- data/doc/FAQ +6 -6
- data/doc/meta/announcement.txt +5 -19
- data/doc/tutorial/todolist.html +47 -57
- data/doc/tutorial/todolist.mkd +47 -55
- data/examples/memleak_detector.rb +31 -0
- data/examples/todolist/src/controller/main.rb +14 -13
- data/examples/todolist/src/element/page.rb +2 -2
- data/examples/todolist/src/model.rb +2 -2
- data/examples/todolist/todolist.db +0 -4
- data/examples/whywiki/main.rb +2 -2
- data/examples/whywiki/template/edit.xhtml +1 -1
- data/examples/whywiki/template/show.xhtml +3 -3
- data/lib/proto/src/controller/main.rb +18 -1
- data/lib/proto/template/index.xhtml +11 -2
- data/lib/ramaze.rb +1 -1
- data/lib/ramaze/action.rb +104 -5
- data/lib/ramaze/action/render.rb +54 -0
- data/lib/ramaze/adapter.rb +2 -1
- data/lib/ramaze/adapter/mongrel.rb +13 -4
- data/lib/ramaze/cache.rb +17 -8
- data/lib/ramaze/cache/memcached.rb +1 -5
- data/lib/ramaze/controller.rb +51 -18
- data/lib/ramaze/controller/resolve.rb +19 -14
- data/lib/ramaze/dispatcher.rb +13 -16
- data/lib/ramaze/dispatcher/action.rb +2 -3
- data/lib/ramaze/dispatcher/error.rb +8 -3
- data/lib/ramaze/dispatcher/file.rb +1 -4
- data/lib/ramaze/error.rb +5 -5
- data/lib/ramaze/global.rb +7 -1
- data/lib/ramaze/global/globalstruct.rb +1 -3
- data/lib/ramaze/helper/aspect.rb +8 -10
- data/lib/ramaze/helper/cgi.rb +21 -3
- data/lib/ramaze/helper/identity.rb +4 -6
- data/lib/ramaze/helper/link.rb +4 -4
- data/lib/ramaze/helper/pager.rb +316 -0
- data/lib/ramaze/helper/partial.rb +37 -0
- data/lib/ramaze/helper/stack.rb +1 -1
- data/lib/ramaze/inform.rb +9 -0
- data/lib/ramaze/inform/hub.rb +5 -0
- data/lib/ramaze/inform/informer.rb +12 -6
- data/lib/ramaze/inform/informing.rb +32 -7
- data/lib/ramaze/inform/knotify.rb +21 -0
- data/lib/ramaze/inform/xosd.rb +58 -24
- data/lib/ramaze/sourcereload.rb +30 -1
- data/lib/ramaze/template.rb +33 -12
- data/lib/ramaze/template/amrita2.rb +21 -20
- data/lib/ramaze/template/erubis.rb +18 -14
- data/lib/ramaze/template/ezamar.rb +15 -26
- data/lib/ramaze/template/ezamar/element.rb +1 -1
- data/lib/ramaze/template/ezamar/engine.rb +45 -36
- data/lib/ramaze/template/ezamar/morpher.rb +3 -3
- data/lib/ramaze/template/ezamar/render_partial.rb +26 -0
- data/lib/ramaze/template/haml.rb +23 -18
- data/lib/ramaze/template/liquid.rb +5 -3
- data/lib/ramaze/template/markaby.rb +14 -11
- data/lib/ramaze/template/remarkably.rb +11 -5
- data/lib/ramaze/tool/localize.rb +12 -4
- data/lib/ramaze/tool/tidy.rb +26 -23
- data/lib/ramaze/trinity/request.rb +11 -7
- data/lib/ramaze/trinity/session.rb +24 -8
- data/lib/ramaze/version.rb +1 -1
- data/rake_tasks/maintaince.rake +136 -11
- data/spec/examples/templates/template_liquid.rb +6 -3
- data/spec/examples/todolist.rb +1 -2
- data/spec/helper/minimal.rb +7 -7
- data/spec/ramaze/action/basics.rb +19 -0
- data/spec/ramaze/action/render.rb +18 -0
- data/spec/ramaze/controller.rb +1 -1
- data/spec/ramaze/controller/template_resolving.rb +1 -1
- data/spec/ramaze/dispatcher/file.rb +24 -0
- data/spec/ramaze/error.rb +28 -29
- data/spec/ramaze/helper/cgi.rb +43 -0
- data/spec/ramaze/helper/pager.rb +27 -0
- data/spec/ramaze/helper/partial.rb +38 -0
- data/spec/ramaze/helper/template/partial.xhtml +1 -0
- data/spec/ramaze/inform/informer.rb +1 -1
- data/spec/ramaze/localize.rb +1 -1
- data/spec/ramaze/morpher.rb +3 -3
- data/spec/ramaze/request.rb +1 -3
- data/spec/ramaze/template.rb +9 -7
- data/spec/ramaze/template/haml.rb +2 -1
- metadata +21 -7
- data/examples/todolist/public/404.jpg +0 -0
- data/examples/todolist/public/error.xhtml +0 -74
- data/lib/ramaze/controller/render.rb +0 -90
data/lib/ramaze/inform.rb
CHANGED
@@ -5,8 +5,17 @@ require 'ramaze/inform/informing'
|
|
5
5
|
require 'ramaze/inform/hub'
|
6
6
|
require 'ramaze/inform/informer'
|
7
7
|
|
8
|
+
begin
|
9
|
+
require 'win32console' if RUBY_PLATFORM =~ /win32/i
|
10
|
+
rescue LoadError => ex
|
11
|
+
puts ex
|
12
|
+
puts "For nice colors on windows, please `gem install win32console`"
|
13
|
+
Ramaze::Informer.trait[:colorize] = false
|
14
|
+
end
|
15
|
+
|
8
16
|
module Ramaze
|
9
17
|
autoload :Analogger, "ramaze/inform/analogger.rb"
|
18
|
+
autoload :Knotify, "ramaze/inform/knotify.rb"
|
10
19
|
autoload :Syslog, "ramaze/inform/syslog.rb"
|
11
20
|
autoload :Growl, "ramaze/inform/growl.rb"
|
12
21
|
autoload :Xosd, "ramaze/inform/xosd.rb"
|
data/lib/ramaze/inform/hub.rb
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
# Copyright (c) 2006 Michael Fellinger m.fellinger@gmail.com
|
2
2
|
# All files in this distribution are subject to the terms of the Ruby license.
|
3
3
|
|
4
|
+
require 'set'
|
5
|
+
|
4
6
|
module Ramaze
|
5
7
|
class LogHub
|
6
8
|
include Informing
|
7
9
|
|
8
10
|
attr_accessor :loggers
|
11
|
+
attr_accessor :ignored_tags
|
9
12
|
|
10
13
|
def initialize(*loggers)
|
11
14
|
@loggers = loggers
|
15
|
+
@ignored_tags = Set.new
|
12
16
|
@loggers.map! do |logger|
|
13
17
|
next(nil) if logger == self
|
14
18
|
logger.is_a?(Class) ? logger.new : logger
|
@@ -18,6 +22,7 @@ module Ramaze
|
|
18
22
|
end
|
19
23
|
|
20
24
|
def inform(tag, *args)
|
25
|
+
return if @ignored_tags.include?(tag)
|
21
26
|
@loggers.each do |logger|
|
22
27
|
logger.inform(tag, *args)
|
23
28
|
end
|
@@ -5,7 +5,10 @@ module Ramaze
|
|
5
5
|
class Informer
|
6
6
|
include Informing
|
7
7
|
|
8
|
-
attr_accessor :
|
8
|
+
attr_accessor :out, :colorize
|
9
|
+
|
10
|
+
# Should Ramaze try to use colors?
|
11
|
+
trait :colorize => true
|
9
12
|
|
10
13
|
# parameter for Time.now.strftime
|
11
14
|
trait :timestamp => "%Y-%m-%d %H:%M:%S"
|
@@ -14,14 +17,16 @@ module Ramaze
|
|
14
17
|
trait :format => "[%time] %prefix %text"
|
15
18
|
|
16
19
|
# Which tag should be in what color
|
17
|
-
|
20
|
+
COLORS = {
|
18
21
|
:info => :green,
|
19
22
|
:debug => :yellow,
|
20
23
|
:warn => :red,
|
21
24
|
:error => :red,
|
22
25
|
}
|
23
26
|
|
24
|
-
def initialize(out = $stdout
|
27
|
+
def initialize(out = $stdout)
|
28
|
+
@colorize = false
|
29
|
+
|
25
30
|
@out =
|
26
31
|
case out
|
27
32
|
when STDOUT, :stdout, 'stdout'
|
@@ -34,12 +39,13 @@ module Ramaze
|
|
34
39
|
if out.respond_to?(:puts)
|
35
40
|
out
|
36
41
|
else
|
37
|
-
colorize = false
|
38
42
|
File.open(out.to_s, 'ab+')
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
42
|
-
|
46
|
+
if @out.respond_to?(:tty?) and class_trait[:colorize]
|
47
|
+
@colorize = @out.tty?
|
48
|
+
end
|
43
49
|
end
|
44
50
|
|
45
51
|
def shutdown
|
@@ -56,7 +62,7 @@ module Ramaze
|
|
56
62
|
prefix = tag.to_s.upcase.ljust(5)
|
57
63
|
|
58
64
|
if @colorize
|
59
|
-
color =
|
65
|
+
color = COLORS[tag] ||= :white
|
60
66
|
prefix.replace prefix.send(color)
|
61
67
|
end
|
62
68
|
|
@@ -2,6 +2,18 @@
|
|
2
2
|
# All files in this distribution are subject to the terms of the Ruby license.
|
3
3
|
|
4
4
|
module Ramaze
|
5
|
+
|
6
|
+
# This module provides a basic skeleton for your own loggers to be compatible.
|
7
|
+
# The minimal usage is like this:
|
8
|
+
#
|
9
|
+
# class MyLogger
|
10
|
+
# include Informing
|
11
|
+
#
|
12
|
+
# def inform(tag, *args)
|
13
|
+
# p tag => args
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
|
5
17
|
module Informing
|
6
18
|
def tag_inform(tag, meth, *strings)
|
7
19
|
strings.each do |string|
|
@@ -10,24 +22,33 @@ module Ramaze
|
|
10
22
|
end
|
11
23
|
end
|
12
24
|
|
13
|
-
|
14
|
-
|
25
|
+
# Converts everything given to strings and passes them on with :info
|
26
|
+
|
27
|
+
def info(*objects)
|
28
|
+
tag_inform(:info, :to_s, *objects)
|
15
29
|
end
|
16
30
|
|
17
|
-
|
18
|
-
|
31
|
+
# Converts everything given to strings and passes them on with :warn
|
32
|
+
|
33
|
+
def warn(*objects)
|
34
|
+
tag_inform(:warn, :to_s, *objects)
|
19
35
|
end
|
20
36
|
|
21
|
-
|
22
|
-
|
37
|
+
# inspects objects if they are no strings. Tag is :debug
|
38
|
+
|
39
|
+
def debug(*objects)
|
40
|
+
tag_inform(:debug, :inspect, *objects)
|
23
41
|
end
|
24
42
|
|
25
43
|
alias << debug
|
26
44
|
|
45
|
+
# Takes either an Exception or just a String, formats backtraces to be a bit
|
46
|
+
# more readable and passes all of this on to tag_inform :error
|
47
|
+
|
27
48
|
def error(ex)
|
28
49
|
if ex.respond_to?(:exception)
|
29
50
|
message = ex.backtrace[0..Global.backtrace_size]
|
30
|
-
message.map
|
51
|
+
message.map{|m| m.gsub!(/^#{Dir.pwd}/, '.') }
|
31
52
|
message.unshift(ex.inspect)
|
32
53
|
else
|
33
54
|
message = ex.to_s
|
@@ -35,10 +56,14 @@ module Ramaze
|
|
35
56
|
tag_inform(:error, :to_s, *message)
|
36
57
|
end
|
37
58
|
|
59
|
+
# raises
|
60
|
+
|
38
61
|
def inform(*args)
|
39
62
|
raise "#inform should be implemented by an instance including this module (#{self})"
|
40
63
|
end
|
41
64
|
|
65
|
+
# nothing
|
66
|
+
|
42
67
|
def shutdown
|
43
68
|
end
|
44
69
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Copyright (c) 2006 Michael Fellinger m.fellinger@gmail.com
|
2
|
+
# All files in this distribution are subject to the terms of the Ruby license.
|
3
|
+
|
4
|
+
module Ramaze
|
5
|
+
|
6
|
+
class Knotify
|
7
|
+
include Informing
|
8
|
+
|
9
|
+
trait :present => 16
|
10
|
+
|
11
|
+
# Please see for more information:
|
12
|
+
# http://lukeplant.me.uk/articles.php?id=3
|
13
|
+
def inform(tag, *messages)
|
14
|
+
present = class_trait[:present]
|
15
|
+
tag = tag.to_s.capitalize
|
16
|
+
messages.flatten.each do |message|
|
17
|
+
system(%{dcop knotify default notify Ramaze "#{tag}" "#{message}" '' '' #{present} 0})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/ramaze/inform/xosd.rb
CHANGED
@@ -2,40 +2,74 @@
|
|
2
2
|
# All files in this distribution are subject to the terms of the Ruby license.
|
3
3
|
|
4
4
|
require 'xosd'
|
5
|
+
require 'thread'
|
5
6
|
|
6
7
|
module Ramaze
|
7
8
|
|
8
9
|
class Xosd < ::Xosd
|
9
|
-
|
10
|
+
attr_accessor :options
|
10
11
|
|
11
|
-
|
12
|
-
trait :lines => 3
|
12
|
+
include Informing
|
13
13
|
|
14
|
-
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
14
|
+
DEFAULT = {
|
15
|
+
:font_size => 20,
|
16
|
+
:font => "-*-*-*-*-*-*-%d-*-*-*-*-*-*-*",
|
17
|
+
:align => 'center',
|
18
|
+
:color => '#FFFFFF',
|
19
|
+
:lines => 3,
|
20
|
+
:valign => 'top',
|
21
|
+
:timeout => 3,
|
22
|
+
:outline_color => "#000000",
|
23
|
+
:outline_width => 1,
|
24
|
+
:vertical_offset => 20,
|
25
|
+
:colors => {
|
26
|
+
:error => "#FF0000",
|
27
|
+
:info => "#00FF00",
|
28
|
+
:warn => "#EAA61E",
|
29
|
+
:debug => "#FFFF00"
|
30
|
+
},
|
19
31
|
}
|
20
32
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
self.vertical_offset = 20
|
32
|
-
end
|
33
|
+
# keys to ignore when setting the options to the instance.
|
34
|
+
IGNORE = [:colors, :font_size, :lines]
|
35
|
+
|
36
|
+
# Here new messages are pushed to eventually displaying them.
|
37
|
+
QUEUE = Queue.new
|
38
|
+
|
39
|
+
def initialize(options = {})
|
40
|
+
@options = DEFAULT.merge(options)
|
41
|
+
|
42
|
+
super(@options[:lines])
|
33
43
|
|
34
|
-
|
35
|
-
|
44
|
+
@options.each do |key, value|
|
45
|
+
next if IGNORE.include?(key)
|
46
|
+
value %= @options[:font_size] if key == :font
|
47
|
+
send("#{key}=", value)
|
48
|
+
end
|
49
|
+
|
50
|
+
Thread.new(self) do |xosd|
|
51
|
+
loop do
|
52
|
+
items = []
|
53
|
+
lines = xosd.options[:lines]
|
54
|
+
items << QUEUE.shift until QUEUE.empty? or items.size >= lines
|
55
|
+
|
56
|
+
unless items.empty?
|
57
|
+
# pad up with empty lines to avoid dragging around old messages.
|
58
|
+
items << [:info, ' '] until items.size >= lines
|
59
|
+
|
60
|
+
items.each_with_index do |(tag, message), i|
|
61
|
+
xosd.color = xosd.options[:colors][tag.to_sym]
|
62
|
+
xosd.display(message, i)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
sleep xosd.options[:timeout]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
36
69
|
|
37
|
-
|
38
|
-
|
70
|
+
def inform(tag, *messages)
|
71
|
+
messages.each do |message|
|
72
|
+
QUEUE << [tag, message]
|
39
73
|
end
|
40
74
|
end
|
41
75
|
end
|
data/lib/ramaze/sourcereload.rb
CHANGED
@@ -9,7 +9,8 @@ module Ramaze
|
|
9
9
|
|
10
10
|
def initialize interval = 1, reload_glob = %r{(^\./)|#{Dir.pwd}|ramaze}
|
11
11
|
@interval, @reload_glob = interval, reload_glob
|
12
|
-
@
|
12
|
+
@map, @files, @paths = [], [], []
|
13
|
+
@mtimes = {}
|
13
14
|
end
|
14
15
|
|
15
16
|
def start
|
@@ -45,6 +46,7 @@ module Ramaze
|
|
45
46
|
#
|
46
47
|
|
47
48
|
def reload
|
49
|
+
SourceReloadHooks.before_reload
|
48
50
|
all_reload_files.each do |file|
|
49
51
|
mtime = mtime(file)
|
50
52
|
|
@@ -53,6 +55,7 @@ module Ramaze
|
|
53
55
|
Inform.debug("reload #{file}")
|
54
56
|
@mtimes[file] = mtime if safe_load(file)
|
55
57
|
end
|
58
|
+
SourceReloadHooks.after_reload
|
56
59
|
end
|
57
60
|
|
58
61
|
def all_reload_files
|
@@ -79,11 +82,37 @@ module Ramaze
|
|
79
82
|
end
|
80
83
|
|
81
84
|
def safe_load(file)
|
85
|
+
SourceReloadHooks.before_safe_load(file)
|
82
86
|
load(file)
|
87
|
+
SourceReloadHooks.after_safe_load_succeed(file)
|
83
88
|
true
|
84
89
|
rescue Object => ex
|
85
90
|
Inform.error(ex)
|
91
|
+
SourceReloadHooks.after_safe_load_failed(file, ex)
|
86
92
|
false
|
87
93
|
end
|
88
94
|
end
|
95
|
+
|
96
|
+
# Holds hooks that are called before and after #reload and #safe_load
|
97
|
+
|
98
|
+
module SourceReloadHooks
|
99
|
+
module_function
|
100
|
+
|
101
|
+
def before_reload
|
102
|
+
end
|
103
|
+
|
104
|
+
def after_reload
|
105
|
+
end
|
106
|
+
|
107
|
+
def before_safe_load(file)
|
108
|
+
end
|
109
|
+
|
110
|
+
def after_safe_load_succeed(file)
|
111
|
+
Cache.compiled.clear
|
112
|
+
Cache.resolved.clear
|
113
|
+
end
|
114
|
+
|
115
|
+
def after_safe_load_failed(file, error)
|
116
|
+
end
|
117
|
+
end
|
89
118
|
end
|
data/lib/ramaze/template.rb
CHANGED
@@ -7,31 +7,35 @@
|
|
7
7
|
module Ramaze
|
8
8
|
module Template
|
9
9
|
|
10
|
+
COMPILED = {} unless defined?(COMPILED)
|
11
|
+
|
12
|
+
# Place register_engine puts the class and extensions for templating engines
|
13
|
+
|
14
|
+
ENGINES = {} unless defined?(ENGINES)
|
15
|
+
|
10
16
|
%w[ Amrita2 Erubis Haml Liquid Markaby Remarkably ].each do |const|
|
11
17
|
autoload(const, "ramaze/template/#{const.downcase}")
|
12
18
|
end
|
13
19
|
|
14
|
-
# The superclass for all templates,
|
15
|
-
#
|
20
|
+
# The superclass for all templates, contains the shared behaviour of
|
21
|
+
# the templates and includes Ramaze::Helper
|
16
22
|
|
17
23
|
class Template
|
18
24
|
include Ramaze::Helper
|
19
25
|
|
20
26
|
class << self
|
21
27
|
|
22
|
-
#
|
23
|
-
#
|
28
|
+
# calls result_and_file with the given action and returns the first of
|
29
|
+
# the result of the controller or content of the file.
|
24
30
|
|
25
31
|
def reaction_or_file action
|
26
|
-
|
27
|
-
|
28
|
-
if file = action.template
|
29
|
-
File.read(file)
|
30
|
-
else
|
31
|
-
reaction.to_s
|
32
|
-
end
|
32
|
+
result_and_file(action).reverse.compact.first
|
33
33
|
end
|
34
34
|
|
35
|
+
# Takes an Action and returns the result from sending the action.method
|
36
|
+
# to the controller via render_method and reads the contents of the file
|
37
|
+
# if given.
|
38
|
+
|
35
39
|
def result_and_file(action)
|
36
40
|
result = render_method(action)
|
37
41
|
|
@@ -42,9 +46,26 @@ module Ramaze
|
|
42
46
|
[result, content]
|
43
47
|
end
|
44
48
|
|
49
|
+
# returns nil if no method is on the action, otherwise it will send the
|
50
|
+
# action and optional parameters to the controller via __send__ and
|
51
|
+
# return the unaltered result
|
52
|
+
|
45
53
|
def render_method(action)
|
46
54
|
return unless method = action.method
|
47
|
-
action.
|
55
|
+
action.instance.__send__(method, *action.params)
|
56
|
+
end
|
57
|
+
|
58
|
+
# This is a wrapper to use Global.compile without even thinking about it.
|
59
|
+
# Don't use it if your engine is not based on a compile/eval principle.
|
60
|
+
|
61
|
+
def wrap_compile(action, template = nil)
|
62
|
+
template ||= reaction_or_file(action).to_s
|
63
|
+
|
64
|
+
if Global.compile
|
65
|
+
Cache.compiled[action.relaxed_hash] ||= compile(action, template)
|
66
|
+
else
|
67
|
+
compile(action, template)
|
68
|
+
end
|
48
69
|
end
|
49
70
|
end
|
50
71
|
end
|
@@ -3,32 +3,33 @@
|
|
3
3
|
|
4
4
|
require 'amrita2/template'
|
5
5
|
|
6
|
-
module Ramaze
|
6
|
+
module Ramaze
|
7
|
+
module Template
|
8
|
+
# Is responsible for compiling a template using the Amrita2 templating engine.
|
7
9
|
|
8
|
-
|
10
|
+
class Amrita2 < Template
|
9
11
|
|
10
|
-
|
12
|
+
ENGINES[self] = %w[ amrita amr ]
|
11
13
|
|
12
|
-
|
14
|
+
class << self
|
13
15
|
|
14
|
-
|
16
|
+
# Takes an Action
|
17
|
+
# The file is rendered using Amrita2::TemplateFile.
|
18
|
+
# The Controller is used as the object for expansion.
|
19
|
+
#
|
20
|
+
# The parameters are set to @params in the controller before expansion.
|
15
21
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# The parameters are set to @params in the controller before expansion.
|
22
|
+
def transform action
|
23
|
+
instance, params, file =
|
24
|
+
action.instance, action.params, action.template
|
25
|
+
raise_no_action(action) unless file
|
21
26
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
out = ''
|
29
|
-
controller.instance_variable_set('@params', params)
|
30
|
-
template.expand(out, controller)
|
31
|
-
out
|
27
|
+
template = ::Amrita2::TemplateFile.new(file)
|
28
|
+
out = ''
|
29
|
+
instance.instance_variable_set('@params', params)
|
30
|
+
template.expand(out, instance)
|
31
|
+
out
|
32
|
+
end
|
32
33
|
end
|
33
34
|
end
|
34
35
|
end
|