yard-cucumber 3.1.0 → 4.0.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 +5 -5
- data/.yardopts +1 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +20 -17
- data/History.txt +13 -0
- data/README.md +10 -10
- data/example/scenario_outline.feature +8 -2
- data/example/step_definitions/example.step.rb +13 -0
- data/example/transform.feature +5 -0
- data/lib/cucumber/city_builder.rb +64 -15
- data/lib/templates/default/featuretags/html/namespace.erb +32 -4
- data/lib/templates/default/fulldoc/html/full_list_features.erb +1 -1
- data/lib/templates/default/fulldoc/html/full_list_tags.erb +1 -1
- data/lib/templates/default/fulldoc/html/js/cucumber.js +9 -0
- data/lib/templates/default/fulldoc/html/setup.rb +26 -4
- data/lib/templates/default/tag/html/alpha_table.erb +2 -1
- data/lib/yard-cucumber.rb +1 -0
- data/lib/yard-cucumber/version.rb +1 -1
- data/lib/yard/code_objects/cucumber/base.rb +1 -9
- data/lib/yard/code_objects/cucumber/feature.rb +7 -9
- data/lib/yard/code_objects/cucumber/namespace_object.rb +55 -49
- data/lib/yard/code_objects/cucumber/scenario.rb +6 -10
- data/lib/yard/code_objects/cucumber/scenario_outline.rb +16 -23
- data/lib/yard/code_objects/cucumber/step.rb +12 -9
- data/lib/yard/code_objects/cucumber/tag.rb +17 -13
- data/lib/yard/code_objects/step_definition.rb +3 -6
- data/lib/yard/code_objects/step_transform.rb +3 -6
- data/lib/yard/code_objects/step_transformer.rb +1 -4
- data/lib/yard/handlers/constant_transform_handler.rb +98 -0
- data/lib/yard/handlers/cucumber/feature_handler.rb +1 -1
- data/lib/yard/handlers/step_transform_handler.rb +22 -3
- data/yard-cucumber.gemspec +2 -2
- metadata +44 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e15515841dcdc314151768f94ca174cc323c9cd40162fa6e475a7764e33a3190
|
4
|
+
data.tar.gz: 895ad012764d4bc979f22c9bc3476a1585496c3b89b071b76d37fefa2c07ae3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c10f928e6030dbc310b4ca093c24c0e96aeb85244ad7b5441fe44af9ce3b77ec552f85f3e7484e289337abc16aebd03e4c6907b46f218122c1b266608e8d9dd
|
7
|
+
data.tar.gz: fe07b11b56211fd1a1c5f82e9ab08ec9dccab4d6ccf5a1dc2365a15164a80c49cf51b94b1ae3e24e3bb259380346d86a6b778f9429b695e6f85a66187e0a9189
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--plugin yard-cucumber
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,42 +1,45 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
yard-cucumber (3.
|
5
|
-
cucumber (
|
6
|
-
gherkin (
|
4
|
+
yard-cucumber (3.1.0)
|
5
|
+
cucumber (>= 2.0, < 4.0)
|
6
|
+
gherkin (>= 4.0, < 6.0)
|
7
7
|
yard (~> 0.8, >= 0.8.1)
|
8
8
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
builder (3.2.
|
13
|
-
cucumber (2.
|
12
|
+
builder (3.2.3)
|
13
|
+
cucumber (2.2.0)
|
14
14
|
builder (>= 2.1.2)
|
15
|
-
cucumber-core (~> 1.
|
15
|
+
cucumber-core (~> 1.3.0)
|
16
16
|
cucumber-wire (~> 0.0.1)
|
17
17
|
diff-lcs (>= 1.1.3)
|
18
|
-
|
18
|
+
event-bus (~> 0.1.0)
|
19
|
+
gherkin3 (~> 3.1.0)
|
19
20
|
multi_json (>= 1.7.5, < 2.0)
|
20
21
|
multi_test (>= 0.1.2)
|
21
|
-
cucumber-core (1.
|
22
|
-
|
22
|
+
cucumber-core (1.3.1)
|
23
|
+
gherkin3 (~> 3.1.0)
|
23
24
|
cucumber-wire (0.0.1)
|
24
|
-
diff-lcs (1.
|
25
|
-
|
26
|
-
|
25
|
+
diff-lcs (1.3)
|
26
|
+
event-bus (0.1.0)
|
27
|
+
gherkin (5.0.0)
|
28
|
+
gherkin3 (3.1.2)
|
29
|
+
multi_json (1.13.1)
|
27
30
|
multi_test (0.1.2)
|
28
|
-
rake (10.
|
29
|
-
redcarpet (
|
30
|
-
yard (0.9.
|
31
|
+
rake (10.5.0)
|
32
|
+
redcarpet (3.4.0)
|
33
|
+
yard (0.9.12)
|
31
34
|
|
32
35
|
PLATFORMS
|
33
36
|
ruby
|
34
37
|
|
35
38
|
DEPENDENCIES
|
36
|
-
gherkin (
|
39
|
+
gherkin (>= 4.0, < 6.0)
|
37
40
|
rake (~> 10)
|
38
41
|
redcarpet
|
39
42
|
yard-cucumber!
|
40
43
|
|
41
44
|
BUNDLED WITH
|
42
|
-
1.
|
45
|
+
1.16.1
|
data/History.txt
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
=== 4.0.0 / 2018-02-25
|
2
|
+
|
3
|
+
* @wellavelino: Adjusts the indentation of several project classes
|
4
|
+
(https://github.com/burtlo/yard-cucumber/pull/89)
|
5
|
+
* @elhuang: Adding example tags and scenarios counts
|
6
|
+
(https://github.com/burtlo/yard-cucumber/pull/87)
|
7
|
+
* @weh: Update README.md
|
8
|
+
(https://github.com/burtlo/yard-cucumber/pull/84)
|
9
|
+
* @weh: Fix Gem Load Error
|
10
|
+
(https://github.com/burtlo/yard-cucumber/pull/83)
|
11
|
+
* @Stephen-Kaye: Transforms with CONSTANTS
|
12
|
+
(https://github.com/burtlo/yard-cucumber/pull/79)
|
13
|
+
|
1
14
|
=== 3.1.0/ 2016-12-06
|
2
15
|
|
3
16
|
* Updates to Gherkin 4.0 and Cucumber 2.4.0
|
data/README.md
CHANGED
@@ -92,16 +92,16 @@ This can be configured through the yard configuration file `~/.yard/config` to
|
|
92
92
|
add or remove these search fields.
|
93
93
|
|
94
94
|
```yaml
|
95
|
-
--- !
|
96
|
-
:
|
97
|
-
:
|
98
|
-
|
99
|
-
:autoload_plugins: []
|
100
|
-
|
101
|
-
:
|
102
|
-
|
103
|
-
:
|
104
|
-
|
95
|
+
--- !ruby/hash-with-ivars:SymbolHash
|
96
|
+
elements:
|
97
|
+
:load_plugins: true
|
98
|
+
:ignored_plugins: []
|
99
|
+
:autoload_plugins: []
|
100
|
+
:safe_mode: false
|
101
|
+
:"yard-cucumber":
|
102
|
+
menus: [ 'features', 'directories', 'tags', 'steps', 'step definitions' ]
|
103
|
+
ivars:
|
104
|
+
:@symbolize_value: false
|
105
105
|
```
|
106
106
|
|
107
107
|
By default the configuration, yaml format, that is generate by the `yard config`
|
@@ -1,4 +1,4 @@
|
|
1
|
-
@scenario_outlines @bvt
|
1
|
+
@scenario_outlines @bvt @duplicate
|
2
2
|
Feature: Displaying Scenario Outlines
|
3
3
|
As a reader of the documentation I expect that scenario outlines are documented correctly
|
4
4
|
|
@@ -9,6 +9,7 @@ Feature: Displaying Scenario Outlines
|
|
9
9
|
When the customer has purchased the product
|
10
10
|
Then I expect the customer to be a member of the '<Product>' group
|
11
11
|
|
12
|
+
@tagged_examples
|
12
13
|
Examples:
|
13
14
|
| Customer | Product |
|
14
15
|
| Customer A | Product A |
|
@@ -61,6 +62,7 @@ Feature: Displaying Scenario Outlines
|
|
61
62
|
|
62
63
|
# This is an example of a scenario outline in development.
|
63
64
|
# The example table has not been defined yet
|
65
|
+
@fifth
|
64
66
|
Scenario Outline: Example Table Missing
|
65
67
|
When I click on an example row
|
66
68
|
Then I expect <name> to be replaced by the example name
|
@@ -70,6 +72,7 @@ Feature: Displaying Scenario Outlines
|
|
70
72
|
|
71
73
|
# This is an example of a scenario outline in development.
|
72
74
|
# The examples table has been defined, but is missing data.
|
75
|
+
@sixth
|
73
76
|
Scenario Outline: Empty Example Table
|
74
77
|
When I click on an example row
|
75
78
|
Then I expect <name> to be replaced by the example name
|
@@ -79,16 +82,19 @@ Feature: Displaying Scenario Outlines
|
|
79
82
|
Examples:
|
80
83
|
| name | price | denomination |
|
81
84
|
|
85
|
+
@seventh @duplicate
|
82
86
|
Scenario Outline: Multiple Example Table
|
83
87
|
Given that <Customer> is a valid customer
|
84
88
|
And that the product, named '<Product>', is a valid product
|
85
89
|
When the customer has purchased the product
|
86
90
|
Then I expect the customer to be a member of the '<Product>' group
|
87
91
|
|
92
|
+
@groupA @duplicate
|
88
93
|
Examples: Example group A
|
89
94
|
| Customer | Product |
|
90
95
|
| Customer A | Product A |
|
91
96
|
|
97
|
+
@groupB
|
92
98
|
Examples: Example group B
|
93
99
|
| Customer | Product |
|
94
|
-
| Customer B | Product A |
|
100
|
+
| Customer B | Product A |
|
@@ -26,6 +26,13 @@ Transform /^((?:\d{1,2}[\/-]){2}(?:\d\d){1,2})?\s*(\w{3})?\s*(\d{1,2}:\d{2}\s*(?
|
|
26
26
|
"#{date} #{day} #{time}"
|
27
27
|
end
|
28
28
|
|
29
|
+
#
|
30
|
+
# Assign a trnasform to a variable to be interpolated later in a step definition
|
31
|
+
#
|
32
|
+
SUBSTITUTED_FILE = Transform(/^file '([^']*)'$/) do |filepath|
|
33
|
+
"Contents loaded from file"
|
34
|
+
end
|
35
|
+
|
29
36
|
Given /^that (#{CUSTOMER}) is a valid customer$/ do |customer|
|
30
37
|
pending "Customer #{customer} validation"
|
31
38
|
end
|
@@ -100,6 +107,12 @@ When /^the step definition has HTML escaped characters like: "([^"]+)"$/ do |cha
|
|
100
107
|
pending characters
|
101
108
|
end
|
102
109
|
|
110
|
+
#
|
111
|
+
# Step using interpolated transform to replace file reference with contents of file
|
112
|
+
#
|
113
|
+
Then /^the (#{SUBSTITUTED_FILE}) will be replaced with the file contents$/ do |content|
|
114
|
+
pending "File contained #{content}"
|
115
|
+
end
|
103
116
|
|
104
117
|
#
|
105
118
|
# Some details about the helper method that might be picked up in the documentation.
|
data/example/transform.feature
CHANGED
@@ -11,3 +11,8 @@ Feature: Step Transforms
|
|
11
11
|
Scenario: Step Transform uses a constant
|
12
12
|
Given this first step
|
13
13
|
Then I expect that the step, on the step transformer page, will link to the step transform
|
14
|
+
|
15
|
+
@third
|
16
|
+
Scenario: Step Transform uses an interpolated transform
|
17
|
+
Given this first step
|
18
|
+
Then the file './somelocation/somefile.input' will be replaced with the file contents
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Cucumber
|
2
2
|
module Parser
|
3
|
-
class CityBuilder < Gherkin::AstBuilder
|
3
|
+
class CityBuilder < ::Gherkin::AstBuilder
|
4
4
|
|
5
5
|
#
|
6
6
|
# The Gherkin Parser is going to call the various methods within this
|
@@ -74,7 +74,7 @@ module Cucumber
|
|
74
74
|
def find_or_create_tag(tag_name,parent)
|
75
75
|
#log.debug "Processing tag #{tag_name}"
|
76
76
|
tag_code_object = YARD::Registry.all(:tag).find {|tag| tag.value == tag_name } ||
|
77
|
-
YARD::CodeObjects::Cucumber::Tag.new(YARD::CodeObjects::Cucumber::CUCUMBER_TAG_NAMESPACE,tag_name.gsub('@','')) {|t| t.owners = [] ; t.value = tag_name }
|
77
|
+
YARD::CodeObjects::Cucumber::Tag.new(YARD::CodeObjects::Cucumber::CUCUMBER_TAG_NAMESPACE,tag_name.gsub('@','')) {|t| t.owners = [] ; t.value = tag_name ; t.total_scenarios = 0}
|
78
78
|
|
79
79
|
tag_code_object.add_file(@file,parent.line)
|
80
80
|
|
@@ -84,7 +84,7 @@ module Cucumber
|
|
84
84
|
|
85
85
|
#
|
86
86
|
# Each feature found will call this method, generating the feature object.
|
87
|
-
# This is once, as the
|
87
|
+
# This is once, as the gherkin parser does not like multiple feature per
|
88
88
|
# file.
|
89
89
|
#
|
90
90
|
def feature(document)
|
@@ -103,16 +103,25 @@ module Cucumber
|
|
103
103
|
|
104
104
|
feature[:tags].each {|feature_tag| find_or_create_tag(feature_tag[:name],f) }
|
105
105
|
end
|
106
|
-
|
107
|
-
|
106
|
+
|
107
|
+
background(feature[:background]) if feature[:background]
|
108
|
+
|
109
|
+
feature[:children].each do |child|
|
110
|
+
case child[:type]
|
108
111
|
when :Background
|
109
|
-
background(
|
112
|
+
background(child)
|
110
113
|
when :ScenarioOutline
|
111
|
-
scenario_outline(
|
114
|
+
outline = scenario_outline(child)
|
115
|
+
@feature.total_scenarios += outline.scenarios.size
|
112
116
|
when :Scenario
|
113
|
-
scenario(
|
117
|
+
scenario(child)
|
114
118
|
end
|
115
|
-
|
119
|
+
end
|
120
|
+
|
121
|
+
@feature.tags.each do |feature_tag|
|
122
|
+
tag_code_object = YARD::Registry.all(:tag).find {|tag| tag.name.to_s == feature_tag[:name].to_s }
|
123
|
+
tag_code_object.total_scenarios += @feature.total_scenarios
|
124
|
+
end
|
116
125
|
end
|
117
126
|
|
118
127
|
#
|
@@ -172,6 +181,14 @@ module Cucumber
|
|
172
181
|
statement[:steps].each { |s|
|
173
182
|
step(s)
|
174
183
|
}
|
184
|
+
|
185
|
+
# count scenarios for scenario level tags
|
186
|
+
scenario.tags.uniq.each { |scenario_tag|
|
187
|
+
if !scenario.feature.tags.include?(scenario_tag)
|
188
|
+
tag_code_object = YARD::Registry.all(:tag).find {|tag| tag.name.to_s == scenario_tag[:name].to_s }
|
189
|
+
tag_code_object.total_scenarios += 1
|
190
|
+
end
|
191
|
+
}
|
175
192
|
end
|
176
193
|
|
177
194
|
#
|
@@ -197,16 +214,41 @@ module Cucumber
|
|
197
214
|
end
|
198
215
|
|
199
216
|
outline.feature = @feature
|
200
|
-
@feature.scenarios << outline
|
201
217
|
@step_container = outline
|
202
218
|
statement[:steps].each { |s|
|
203
219
|
step(s)
|
204
220
|
}
|
221
|
+
|
205
222
|
statement[:examples].each { |e|
|
206
|
-
examples(e)
|
223
|
+
example = examples(e, outline)
|
224
|
+
}
|
225
|
+
|
226
|
+
@feature.scenarios << outline
|
227
|
+
|
228
|
+
# count scenarios for scenario outline level tags
|
229
|
+
outline.tags.uniq.each { |outline_tag|
|
230
|
+
if !outline.feature.tags.include?(outline_tag)
|
231
|
+
tag_code_object = YARD::Registry.all(:tag).find {|tag| tag.name.to_s == outline_tag[:name].to_s }
|
232
|
+
tag_code_object.total_scenarios += outline.scenarios.size
|
233
|
+
end
|
234
|
+
}
|
235
|
+
|
236
|
+
# count scenarios for example table level tags
|
237
|
+
outline.examples.each { |example|
|
238
|
+
unless !example.tags.any?
|
239
|
+
example.tags.uniq.each { |example_tag|
|
240
|
+
if !outline.feature.tags.include?(example_tag) && !outline.tags.include?(example_tag)
|
241
|
+
tag_code_object = YARD::Registry.all(:tag).find {|tag| tag.name.to_s == example_tag[:name].to_s }
|
242
|
+
tag_code_object.total_scenarios += example.data.size
|
243
|
+
end
|
244
|
+
}
|
245
|
+
end
|
207
246
|
}
|
247
|
+
|
248
|
+
return outline
|
208
249
|
end
|
209
250
|
|
251
|
+
|
210
252
|
#
|
211
253
|
# Examples for a scenario outline are called here. This section differs
|
212
254
|
# from the Cucumber parser because here each of the examples are exploded
|
@@ -214,15 +256,21 @@ module Cucumber
|
|
214
256
|
# later we can ensure that we have all the variations of the scenario
|
215
257
|
# outline defined to be displayed.
|
216
258
|
#
|
217
|
-
def examples(examples)
|
259
|
+
def examples(examples, outline)
|
218
260
|
#log.debug "EXAMPLES"
|
219
|
-
|
261
|
+
return if has_exclude_tags?(examples[:tags].map { |t| t[:name].gsub(/^@/, '') })
|
220
262
|
example = YARD::CodeObjects::Cucumber::ScenarioOutline::Examples.new(:keyword => examples[:keyword],
|
221
263
|
:name => examples[:name],
|
222
264
|
:line => examples[:location][:line],
|
223
265
|
:comments => examples[:comments] ? examples.comments.map{|comment| comment.value}.join("\n") : '',
|
224
|
-
:rows => []
|
225
|
-
|
266
|
+
:rows => [],
|
267
|
+
:tags => [],
|
268
|
+
:scenario => outline )
|
269
|
+
|
270
|
+
unless !examples[:tags].any?
|
271
|
+
examples[:tags].each {|example_tag| find_or_create_tag(example_tag[:name], example)}
|
272
|
+
end
|
273
|
+
|
226
274
|
example.rows = [examples[:tableHeader][:cells].map{ |c| c[:value] }] if examples[:tableHeader]
|
227
275
|
example.rows += matrix(examples[:tableBody]) if examples[:tableBody]
|
228
276
|
|
@@ -282,6 +330,7 @@ module Cucumber
|
|
282
330
|
|
283
331
|
end
|
284
332
|
|
333
|
+
return example
|
285
334
|
end
|
286
335
|
|
287
336
|
#
|
@@ -1,4 +1,7 @@
|
|
1
1
|
<% if @namespace %>
|
2
|
+
<% count = 0 %>
|
3
|
+
<% features.each {|f| count += f.total_scenarios } %>
|
4
|
+
|
2
5
|
<div id="tags" class="requirements">
|
3
6
|
<script type="text/javascript" charset="utf-8">
|
4
7
|
var tag_list = [ <%= tags.collect{|t| "'#{t.value}'" }.join(',') %> ];
|
@@ -18,11 +21,13 @@
|
|
18
21
|
|
19
22
|
$("#tag_search").keyup(function(evt) {
|
20
23
|
updateTagFiltering($("#tag_search")[0].value);
|
24
|
+
updateScenarioCount();
|
21
25
|
});
|
22
26
|
|
23
27
|
|
24
28
|
$("#tag_search").keyup(function(evt) {
|
25
29
|
updateTagFiltering($("#tag_search")[0].value);
|
30
|
+
updateScenarioCount();
|
26
31
|
});
|
27
32
|
|
28
33
|
$(".tag").click(function(evt) {
|
@@ -50,6 +55,7 @@
|
|
50
55
|
|
51
56
|
tagSearchElement.value = (tagSearchElement.value != "" ? tagSearchElement.value + tagModifier : "") + tagToAdd;
|
52
57
|
updateTagFiltering(tagSearchElement.value);
|
58
|
+
updateScenarioCount();
|
53
59
|
|
54
60
|
}
|
55
61
|
});
|
@@ -89,7 +95,7 @@
|
|
89
95
|
|
90
96
|
<div id="features">
|
91
97
|
<div class="title">
|
92
|
-
<span class="name">Features</span>
|
98
|
+
<span class="name">Features<span id="scenario_count"> (<%= count %>)</span></span>
|
93
99
|
</div>
|
94
100
|
<% n = 1 %>
|
95
101
|
<ul style="padding-left: 0px;">
|
@@ -105,9 +111,9 @@
|
|
105
111
|
<% if feature.scenarios %>
|
106
112
|
<ul style="padding-left: 20px;">
|
107
113
|
<% feature.scenarios.each do |scenario| %>
|
108
|
-
<li class="scenario <%= n % 2 == 0 ? 'even' : 'odd' %> <%= feature.tags.collect{|t| t.value }.join(" ") %> <%= scenario.tags.collect{|t| t.value }.join(" ") %>">
|
114
|
+
<li class="scenario <%= n % 2 == 0 ? 'even' : 'odd' %> <%= feature.tags.collect{|t| t.value }.join(" ") %> <%= scenario.tags.collect{|t| t.value }.join(" ") %>" count="<%= scenario.outline? ? 0 : 1 %>">
|
109
115
|
<span class='object_link'>
|
110
|
-
<a href="<%= url_for(scenario.feature,"
|
116
|
+
<a href="<%= url_for(scenario.feature,"scenario_#{scenario.feature.scenarios.index(scenario) }") %>">
|
111
117
|
<%= h scenario.value %>
|
112
118
|
</a>
|
113
119
|
</span>
|
@@ -116,8 +122,30 @@
|
|
116
122
|
- <small><%= stags %></small>
|
117
123
|
<% end %>
|
118
124
|
</li>
|
125
|
+
|
119
126
|
<% n = n == 2 ? 1 : 2 %>
|
120
|
-
|
127
|
+
|
128
|
+
<% if scenario.outline? %>
|
129
|
+
<ul style="padding-left: 40px;">
|
130
|
+
<% scenario.examples.each do |example| %>
|
131
|
+
<li class="scenario <%= n % 2 == 0 ? 'even' : 'odd' %> <%= feature.tags.collect{|t| t.value }.join(" ") %> <%= scenario.tags.collect{|t| t.value }.join(" ") %> <%= example.tags.collect{|t| t.value }.join(" ") %>" count="<%= example.data.size %>">
|
132
|
+
|
133
|
+
<span class='object_link'>
|
134
|
+
<a href="<%= url_for(scenario.feature,"scenario_#{scenario.feature.scenarios.index(scenario) }") %>">
|
135
|
+
<%= example.name.nil? || example.name.empty? ? "Examples" : example.name %>
|
136
|
+
</a>
|
137
|
+
</span>
|
138
|
+
|
139
|
+
<% etags = example.tags.collect{|t| tagify(t) }.join(", ") %>
|
140
|
+
<% if etags && etags != "" %>
|
141
|
+
- <small><%= etags %></small>
|
142
|
+
<% end %>
|
143
|
+
</li>
|
144
|
+
<% n = n == 2 ? 1 : 2 %>
|
145
|
+
<% end %>
|
146
|
+
</ul>
|
147
|
+
<% end %>
|
148
|
+
<% end %>
|
121
149
|
</ul>
|
122
150
|
<% end %>
|
123
151
|
<% end %>
|