nesta 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +11 -11
- data/.hound.yml +2 -0
- data/.travis.yml +2 -1
- data/CHANGES +43 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +18 -23
- data/lib/nesta/app.rb +0 -5
- data/lib/nesta/commands.rb +5 -288
- data/lib/nesta/commands/command.rb +58 -0
- data/lib/nesta/commands/demo.rb +1 -0
- data/lib/nesta/commands/demo/content.rb +38 -0
- data/lib/nesta/commands/edit.rb +21 -0
- data/lib/nesta/commands/new.rb +57 -0
- data/lib/nesta/commands/plugin.rb +1 -0
- data/lib/nesta/commands/plugin/create.rb +82 -0
- data/lib/nesta/commands/theme.rb +3 -0
- data/lib/nesta/commands/theme/create.rb +36 -0
- data/lib/nesta/commands/theme/enable.rb +22 -0
- data/lib/nesta/commands/theme/install.rb +29 -0
- data/lib/nesta/config.rb +1 -6
- data/lib/nesta/models.rb +18 -20
- data/lib/nesta/version.rb +1 -1
- data/nesta.gemspec +1 -0
- data/smoke-test.sh +24 -19
- data/spec/commands/demo/content_spec.rb +65 -0
- data/spec/commands/edit_spec.rb +27 -0
- data/spec/commands/new_spec.rb +88 -0
- data/spec/commands/plugin/create_spec.rb +97 -0
- data/spec/commands/system_spec.rb +25 -0
- data/spec/commands/theme/create_spec.rb +41 -0
- data/spec/commands/theme/enable_spec.rb +44 -0
- data/spec/commands/theme/install_spec.rb +56 -0
- data/spec/config_spec.rb +3 -3
- data/spec/models_spec.rb +43 -25
- data/spec/page_spec.rb +18 -2
- data/spec/spec_helper.rb +23 -0
- data/templates/Gemfile +1 -1
- data/templates/plugins/Gemfile +4 -0
- data/templates/plugins/README.md +13 -0
- data/templates/plugins/Rakefile +58 -0
- data/templates/plugins/gitignore +3 -0
- data/templates/plugins/lib/init.rb +13 -0
- data/templates/plugins/lib/required.rb +3 -0
- data/templates/plugins/lib/version.rb +5 -0
- data/templates/plugins/plugin.gemspec +28 -0
- data/views/analytics.haml +9 -10
- data/views/master.sass +1 -1
- metadata +53 -5
- data/spec/commands_spec.rb +0 -395
@@ -0,0 +1,58 @@
|
|
1
|
+
module Nesta
|
2
|
+
module Commands
|
3
|
+
class UsageError < RuntimeError; end
|
4
|
+
|
5
|
+
module Command
|
6
|
+
def run_process(*args)
|
7
|
+
system(*args)
|
8
|
+
if ! $?.success?
|
9
|
+
message = if $?.exitstatus == 127
|
10
|
+
"#{args[0]} not found"
|
11
|
+
else
|
12
|
+
"'#{args.join(' ')}' failed with status #{$?.exitstatus}"
|
13
|
+
end
|
14
|
+
$stderr.puts "Error: #{message}"
|
15
|
+
exit 1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def fail(message)
|
20
|
+
$stderr.puts "Error: #{message}"
|
21
|
+
exit 1
|
22
|
+
end
|
23
|
+
|
24
|
+
def template_root
|
25
|
+
File.expand_path('../../../templates', File.dirname(__FILE__))
|
26
|
+
end
|
27
|
+
|
28
|
+
def copy_template(src, dest)
|
29
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
30
|
+
template = ERB.new(File.read(File.join(template_root, src)), nil, "-")
|
31
|
+
File.open(dest, 'w') { |file| file.puts template.result(binding) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def copy_templates(templates)
|
35
|
+
templates.each { |src, dest| copy_template(src, dest) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def update_config_yaml(pattern, replacement)
|
39
|
+
configured = false
|
40
|
+
File.open(Nesta::Config.yaml_path, 'r+') do |file|
|
41
|
+
output = ''
|
42
|
+
file.each_line do |line|
|
43
|
+
if configured
|
44
|
+
output << line
|
45
|
+
else
|
46
|
+
output << line.sub(pattern, replacement)
|
47
|
+
configured = true if line =~ pattern
|
48
|
+
end
|
49
|
+
end
|
50
|
+
output << "#{replacement}\n" unless configured
|
51
|
+
file.pos = 0
|
52
|
+
file.print(output)
|
53
|
+
file.truncate(file.pos)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path('demo/content', File.dirname(__FILE__))
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path('../command', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module Nesta
|
4
|
+
module Commands
|
5
|
+
module Demo
|
6
|
+
class Content
|
7
|
+
include Command
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
@dir = 'content-demo'
|
11
|
+
end
|
12
|
+
|
13
|
+
def clone_or_update_repository
|
14
|
+
repository = 'git://github.com/gma/nesta-demo-content.git'
|
15
|
+
path = Nesta::Path.local(@dir)
|
16
|
+
if File.exist?(path)
|
17
|
+
FileUtils.cd(path) { run_process('git', 'pull', 'origin', 'master') }
|
18
|
+
else
|
19
|
+
run_process('git', 'clone', repository, path)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def configure_git_to_ignore_repo
|
24
|
+
excludes = Nesta::Path.local('.git/info/exclude')
|
25
|
+
if File.exist?(excludes) && File.read(excludes).scan(@dir).empty?
|
26
|
+
File.open(excludes, 'a') { |file| file.puts @dir }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def execute
|
31
|
+
clone_or_update_repository
|
32
|
+
configure_git_to_ignore_repo
|
33
|
+
update_config_yaml(/^\s*#?\s*content:.*/, "content: #{@dir}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path('command', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module Nesta
|
4
|
+
module Commands
|
5
|
+
class Edit
|
6
|
+
include Command
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
@filename = Nesta::Config.page_path(args.shift)
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute
|
13
|
+
editor = ENV.fetch('EDITOR')
|
14
|
+
rescue IndexError
|
15
|
+
$stderr.puts "No editor: set EDITOR environment variable"
|
16
|
+
else
|
17
|
+
run_process(editor, @filename)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require File.expand_path('command', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module Nesta
|
4
|
+
module Commands
|
5
|
+
class New
|
6
|
+
include Command
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
path = args.shift
|
10
|
+
options = args.shift || {}
|
11
|
+
path.nil? && (raise UsageError.new('path not specified'))
|
12
|
+
if File.exist?(path)
|
13
|
+
raise RuntimeError.new("#{path} already exists")
|
14
|
+
end
|
15
|
+
@path = path
|
16
|
+
@options = options
|
17
|
+
end
|
18
|
+
|
19
|
+
def make_directories
|
20
|
+
%w[content/attachments content/pages].each do |dir|
|
21
|
+
FileUtils.mkdir_p(File.join(@path, dir))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def have_rake_tasks?
|
26
|
+
@options['vlad']
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_repository
|
30
|
+
FileUtils.cd(@path) do
|
31
|
+
File.open('.gitignore', 'w') do |file|
|
32
|
+
file.puts %w[._* .*.swp .bundle .DS_Store .sass-cache].join("\n")
|
33
|
+
end
|
34
|
+
run_process('git', 'init')
|
35
|
+
run_process('git', 'add', '.')
|
36
|
+
run_process('git', 'commit', '-m', 'Initial commit')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def execute
|
41
|
+
make_directories
|
42
|
+
templates = {
|
43
|
+
'config.ru' => "#{@path}/config.ru",
|
44
|
+
'config/config.yml' => "#{@path}/config/config.yml",
|
45
|
+
'index.haml' => "#{@path}/content/pages/index.haml",
|
46
|
+
'Gemfile' => "#{@path}/Gemfile"
|
47
|
+
}
|
48
|
+
templates['Rakefile'] = "#{@path}/Rakefile" if have_rake_tasks?
|
49
|
+
if @options['vlad']
|
50
|
+
templates['config/deploy.rb'] = "#{@path}/config/deploy.rb"
|
51
|
+
end
|
52
|
+
copy_templates(templates)
|
53
|
+
create_repository if @options['git']
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path('plugin/create', File.dirname(__FILE__))
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require File.expand_path('../command', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module Nesta
|
4
|
+
module Commands
|
5
|
+
module Plugin
|
6
|
+
class Create
|
7
|
+
include Command
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
name = args.shift
|
11
|
+
name.nil? && (raise UsageError.new('name not specified'))
|
12
|
+
@name = name
|
13
|
+
@gem_name = "nesta-plugin-#{name}"
|
14
|
+
if File.exist?(@gem_name)
|
15
|
+
raise RuntimeError.new("#{@gem_name} already exists")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def lib_path(*parts)
|
20
|
+
File.join(@gem_name, 'lib', *parts)
|
21
|
+
end
|
22
|
+
|
23
|
+
def module_name
|
24
|
+
module_names.join('::')
|
25
|
+
end
|
26
|
+
|
27
|
+
def nested_module_definition_with_version
|
28
|
+
indent_level = 2
|
29
|
+
indent_with = ' '
|
30
|
+
|
31
|
+
lines = module_names.map { |name| "module #{name}\n" }
|
32
|
+
indent_levels = 0.upto(module_names.size - 1).to_a
|
33
|
+
|
34
|
+
lines << "VERSION = '0.1.0'\n"
|
35
|
+
indent_levels << module_names.size
|
36
|
+
|
37
|
+
(module_names.size - 1).downto(0).each do |indent_level|
|
38
|
+
lines << "end\n"
|
39
|
+
indent_levels << indent_level
|
40
|
+
end
|
41
|
+
|
42
|
+
''.tap do |code|
|
43
|
+
lines.each_with_index do |line, i|
|
44
|
+
code << ' ' * (indent_levels[i] + 2) + line
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def make_directories
|
50
|
+
FileUtils.mkdir_p(File.join(@gem_name, 'lib', @gem_name))
|
51
|
+
end
|
52
|
+
|
53
|
+
def gem_path(path)
|
54
|
+
File.join(@gem_name, path)
|
55
|
+
end
|
56
|
+
|
57
|
+
def execute
|
58
|
+
make_directories
|
59
|
+
copy_templates(
|
60
|
+
'plugins/README.md' => gem_path('README.md'),
|
61
|
+
'plugins/gitignore' => gem_path('.gitignore'),
|
62
|
+
'plugins/plugin.gemspec' => gem_path("#{@gem_name}.gemspec"),
|
63
|
+
'plugins/Gemfile' => gem_path('Gemfile'),
|
64
|
+
'plugins/lib/required.rb' => gem_path("lib/#{@gem_name}.rb"),
|
65
|
+
'plugins/lib/version.rb' => gem_path("lib/#{@gem_name}/version.rb"),
|
66
|
+
'plugins/lib/init.rb' => gem_path("lib/#{@gem_name}/init.rb"),
|
67
|
+
'plugins/Rakefile' => gem_path('Rakefile')
|
68
|
+
)
|
69
|
+
Dir.chdir(@gem_name) do
|
70
|
+
run_process('git', 'init')
|
71
|
+
run_process('git', 'add', '.')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
def module_names
|
77
|
+
@name.split('-').map { |name| name.capitalize }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.expand_path('../command', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module Nesta
|
4
|
+
module Commands
|
5
|
+
module Theme
|
6
|
+
class Create
|
7
|
+
include Command
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
name = args.shift
|
11
|
+
options = args.shift || {}
|
12
|
+
name.nil? && (raise UsageError.new('name not specified'))
|
13
|
+
@name = name
|
14
|
+
@theme_path = Nesta::Path.themes(@name)
|
15
|
+
fail("#{@theme_path} already exists") if File.exist?(@theme_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def make_directories
|
19
|
+
FileUtils.mkdir_p(File.join(@theme_path, 'public', @name))
|
20
|
+
FileUtils.mkdir_p(File.join(@theme_path, 'views'))
|
21
|
+
end
|
22
|
+
|
23
|
+
def execute
|
24
|
+
make_directories
|
25
|
+
copy_templates(
|
26
|
+
'themes/README.md' => "#{@theme_path}/README.md",
|
27
|
+
'themes/app.rb' => "#{@theme_path}/app.rb",
|
28
|
+
'themes/views/layout.haml' => "#{@theme_path}/views/layout.haml",
|
29
|
+
'themes/views/page.haml' => "#{@theme_path}/views/page.haml",
|
30
|
+
'themes/views/master.sass' => "#{@theme_path}/views/master.sass"
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path('../command', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module Nesta
|
4
|
+
module Commands
|
5
|
+
module Theme
|
6
|
+
class Enable
|
7
|
+
include Command
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
name = args.shift
|
11
|
+
options = args.shift || {}
|
12
|
+
name.nil? && (raise UsageError.new('name not specified'))
|
13
|
+
@name = name
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute
|
17
|
+
update_config_yaml(/^\s*#?\s*theme:.*/, "theme: #{@name}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.expand_path('../command', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module Nesta
|
4
|
+
module Commands
|
5
|
+
module Theme
|
6
|
+
class Install
|
7
|
+
include Command
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
url = args.shift
|
11
|
+
options = args.shift || {}
|
12
|
+
url.nil? && (raise UsageError.new('URL not specified'))
|
13
|
+
@url = url
|
14
|
+
@name = File.basename(url, '.git').sub(/nesta-theme-/, '')
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute
|
18
|
+
run_process('git', 'clone', @url, "themes/#{@name}")
|
19
|
+
FileUtils.rm_r(File.join("themes/#{@name}", '.git'))
|
20
|
+
enable
|
21
|
+
end
|
22
|
+
|
23
|
+
def enable
|
24
|
+
Enable.new(@name).execute
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/nesta/config.rb
CHANGED
@@ -90,18 +90,13 @@ module Nesta
|
|
90
90
|
end
|
91
91
|
private_class_method :yaml_exists?
|
92
92
|
|
93
|
-
def self.can_use_yaml?
|
94
|
-
ENV.keys.grep(/^NESTA/).empty? && yaml_exists?
|
95
|
-
end
|
96
|
-
private_class_method :can_use_yaml?
|
97
|
-
|
98
93
|
def self.from_hash(hash, setting)
|
99
94
|
hash.fetch(setting) { raise NotDefined.new(setting) }
|
100
95
|
end
|
101
96
|
private_class_method :from_hash
|
102
97
|
|
103
98
|
def self.from_yaml(setting)
|
104
|
-
raise NotDefined.new(setting) unless
|
99
|
+
raise NotDefined.new(setting) unless yaml_exists?
|
105
100
|
self.yaml_conf ||= YAML::load(ERB.new(IO.read(yaml_path)).result)
|
106
101
|
env_config = self.yaml_conf.fetch(Nesta::App.environment.to_s, {})
|
107
102
|
begin
|
data/lib/nesta/models.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'time'
|
2
2
|
|
3
|
-
Tilt.register Tilt::MarukuTemplate, 'mdown'
|
4
|
-
Tilt.register Tilt::KramdownTemplate, 'mdown'
|
5
|
-
Tilt.register Tilt::BlueClothTemplate, 'mdown'
|
6
|
-
Tilt.register Tilt::RDiscountTemplate, 'mdown'
|
7
|
-
Tilt.register Tilt::RedcarpetTemplate, 'mdown'
|
3
|
+
Tilt.register Tilt::MarukuTemplate, 'mdown', 'md'
|
4
|
+
Tilt.register Tilt::KramdownTemplate, 'mdown', 'md'
|
5
|
+
Tilt.register Tilt::BlueClothTemplate, 'mdown', 'md'
|
6
|
+
Tilt.register Tilt::RDiscountTemplate, 'mdown', 'md'
|
7
|
+
Tilt.register Tilt::RedcarpetTemplate, 'mdown', 'md'
|
8
8
|
|
9
9
|
module Nesta
|
10
10
|
class HeadingNotSet < RuntimeError; end
|
@@ -12,7 +12,7 @@ module Nesta
|
|
12
12
|
class MetadataParseError < RuntimeError; end
|
13
13
|
|
14
14
|
class FileModel
|
15
|
-
FORMATS = [:mdown, :haml, :textile]
|
15
|
+
FORMATS = [:mdown, :md, :haml, :textile]
|
16
16
|
@@page_cache = {}
|
17
17
|
@@filename_cache = {}
|
18
18
|
|
@@ -113,7 +113,7 @@ module Nesta
|
|
113
113
|
(metadata('template') || 'page').to_sym
|
114
114
|
end
|
115
115
|
|
116
|
-
def to_html(scope =
|
116
|
+
def to_html(scope = Object.new)
|
117
117
|
convert_to_html(@format, scope, markup)
|
118
118
|
end
|
119
119
|
|
@@ -128,7 +128,7 @@ module Nesta
|
|
128
128
|
def keywords
|
129
129
|
metadata('keywords')
|
130
130
|
end
|
131
|
-
|
131
|
+
|
132
132
|
def metadata(key)
|
133
133
|
@metadata[key]
|
134
134
|
end
|
@@ -220,7 +220,7 @@ module Nesta
|
|
220
220
|
|
221
221
|
def heading
|
222
222
|
regex = case @format
|
223
|
-
when :mdown
|
223
|
+
when :mdown, :md
|
224
224
|
/^#\s*(.*?)(\s*#+|$)/
|
225
225
|
when :haml
|
226
226
|
/^\s*%h1\s+(.*)/
|
@@ -236,7 +236,7 @@ module Nesta
|
|
236
236
|
rescue HeadingNotSet
|
237
237
|
raise LinkTextNotSet, "Need to link to '#{abspath}' but can't get link text"
|
238
238
|
end
|
239
|
-
|
239
|
+
|
240
240
|
def title
|
241
241
|
metadata('title') || link_text
|
242
242
|
rescue LinkTextNotSet
|
@@ -265,13 +265,13 @@ module Nesta
|
|
265
265
|
def summary
|
266
266
|
if summary_text = metadata("summary")
|
267
267
|
summary_text.gsub!('\n', "\n")
|
268
|
-
convert_to_html(@format,
|
268
|
+
convert_to_html(@format, Object.new, summary_text)
|
269
269
|
end
|
270
270
|
end
|
271
271
|
|
272
272
|
def body_markup
|
273
273
|
case @format
|
274
|
-
when :mdown
|
274
|
+
when :mdown, :md
|
275
275
|
markup.sub(/^#[^#].*$\r?\n(\r?\n)?/, '')
|
276
276
|
when :haml
|
277
277
|
markup.sub(/^\s*%h1\s+.*$\r?\n(\r?\n)?/, '')
|
@@ -280,23 +280,20 @@ module Nesta
|
|
280
280
|
end
|
281
281
|
end
|
282
282
|
|
283
|
-
def body(scope =
|
283
|
+
def body(scope = Object.new)
|
284
284
|
convert_to_html(@format, scope, body_markup)
|
285
285
|
end
|
286
286
|
|
287
287
|
def categories
|
288
288
|
paths = category_strings.map { |specifier| specifier.sub(/:-?\d+$/, '') }
|
289
|
-
|
290
|
-
pages.sort do |x, y|
|
291
|
-
x.link_text.downcase <=> y.link_text.downcase
|
292
|
-
end
|
289
|
+
valid_paths(paths).map { |p| Page.find_by_path(p) }
|
293
290
|
end
|
294
291
|
|
295
292
|
def priority(category)
|
296
293
|
category_string = category_strings.detect do |string|
|
297
294
|
string =~ /^#{category}([,:\s]|$)/
|
298
295
|
end
|
299
|
-
category_string && category_string.split(':', 2)[-1].to_i
|
296
|
+
category_string && category_string.split(':', 2)[-1].to_i
|
300
297
|
end
|
301
298
|
|
302
299
|
def parent
|
@@ -309,6 +306,7 @@ module Nesta
|
|
309
306
|
return parent unless parent.nil?
|
310
307
|
parent_path = File.dirname(parent_path)
|
311
308
|
end
|
309
|
+
return categories.first unless categories.empty?
|
312
310
|
Page.load('index')
|
313
311
|
end
|
314
312
|
end
|
@@ -384,15 +382,15 @@ module Nesta
|
|
384
382
|
rescue EOFError
|
385
383
|
else
|
386
384
|
page = Page.load(path.strip)
|
385
|
+
current_depth = path.scan(INDENT).size
|
387
386
|
if page
|
388
|
-
current_depth = path.scan(INDENT).size
|
389
387
|
if current_depth > depth
|
390
388
|
sub_menu_for_depth(menu, depth) << [page]
|
391
389
|
else
|
392
390
|
sub_menu_for_depth(menu, current_depth) << page
|
393
391
|
end
|
394
|
-
append_menu_item(menu, file, current_depth)
|
395
392
|
end
|
393
|
+
append_menu_item(menu, file, current_depth)
|
396
394
|
end
|
397
395
|
|
398
396
|
def self.sub_menu_for_depth(menu, depth)
|