nesta 0.10.0 → 0.11.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.
- 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)
|