yard-lucid 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +87 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +13 -0
  11. data/bin/setup +8 -0
  12. data/lib/docserver/default/fulldoc/html/js/lucid.js +69 -0
  13. data/lib/docserver/default/layout/html/headers.erb +13 -0
  14. data/lib/docserver/doc_server/full_list/html/full_list.erb +37 -0
  15. data/lib/docserver/doc_server/full_list/html/setup.rb +19 -0
  16. data/lib/lucid/gherkin_repr.rb +307 -0
  17. data/lib/templates/default/feature/html/docstring.erb +3 -0
  18. data/lib/templates/default/feature/html/feature.erb +39 -0
  19. data/lib/templates/default/feature/html/no_steps_defined.erb +1 -0
  20. data/lib/templates/default/feature/html/outline.erb +52 -0
  21. data/lib/templates/default/feature/html/scenario.erb +52 -0
  22. data/lib/templates/default/feature/html/setup.rb +51 -0
  23. data/lib/templates/default/feature/html/steps.erb +37 -0
  24. data/lib/templates/default/feature/html/table.erb +19 -0
  25. data/lib/templates/default/featuredirectory/html/alpha_table.erb +30 -0
  26. data/lib/templates/default/featuredirectory/html/directory.erb +30 -0
  27. data/lib/templates/default/featuredirectory/html/setup.rb +41 -0
  28. data/lib/templates/default/featuretags/html/namespace.erb +127 -0
  29. data/lib/templates/default/featuretags/html/setup.rb +33 -0
  30. data/lib/templates/default/fulldoc/html/css/lucid.css +224 -0
  31. data/lib/templates/default/fulldoc/html/directories.erb +27 -0
  32. data/lib/templates/default/fulldoc/html/full_list_featuredirectories.erb +11 -0
  33. data/lib/templates/default/fulldoc/html/full_list_features.erb +36 -0
  34. data/lib/templates/default/fulldoc/html/full_list_stepdefinitions.erb +20 -0
  35. data/lib/templates/default/fulldoc/html/full_list_steps.erb +20 -0
  36. data/lib/templates/default/fulldoc/html/full_list_tags.erb +16 -0
  37. data/lib/templates/default/fulldoc/html/js/lucid.js +284 -0
  38. data/lib/templates/default/fulldoc/html/setup.rb +199 -0
  39. data/lib/templates/default/layout/html/setup.rb +111 -0
  40. data/lib/templates/default/specifications/html/alpha_table.erb +26 -0
  41. data/lib/templates/default/specifications/html/setup.rb +49 -0
  42. data/lib/templates/default/specifications/html/specifications.erb +46 -0
  43. data/lib/templates/default/steptransformers/html/header.erb +12 -0
  44. data/lib/templates/default/steptransformers/html/index.erb +9 -0
  45. data/lib/templates/default/steptransformers/html/setup.rb +92 -0
  46. data/lib/templates/default/steptransformers/html/transformers.erb +74 -0
  47. data/lib/templates/default/steptransformers/html/undefinedsteps.erb +26 -0
  48. data/lib/templates/default/tag/html/alpha_table.erb +32 -0
  49. data/lib/templates/default/tag/html/setup.rb +26 -0
  50. data/lib/templates/default/tag/html/tag.erb +31 -0
  51. data/lib/yard/code_objects/lucid/base.rb +23 -0
  52. data/lib/yard/code_objects/lucid/feature.rb +12 -0
  53. data/lib/yard/code_objects/lucid/namespace_object.rb +43 -0
  54. data/lib/yard/code_objects/lucid/scenario.rb +20 -0
  55. data/lib/yard/code_objects/lucid/scenario_outline.rb +66 -0
  56. data/lib/yard/code_objects/lucid/step.rb +32 -0
  57. data/lib/yard/code_objects/lucid/tag.rb +21 -0
  58. data/lib/yard/code_objects/step_definition.rb +3 -0
  59. data/lib/yard/code_objects/step_transform.rb +3 -0
  60. data/lib/yard/code_objects/step_transformer.rb +85 -0
  61. data/lib/yard/handlers/lucid/base.rb +19 -0
  62. data/lib/yard/handlers/lucid/feature_handler.rb +102 -0
  63. data/lib/yard/handlers/step_definition_handler.rb +67 -0
  64. data/lib/yard/handlers/step_transform_handler.rb +28 -0
  65. data/lib/yard/parser/lucid/feature.rb +58 -0
  66. data/lib/yard/server/adapter.rb +32 -0
  67. data/lib/yard/server/commands/list_command.rb +25 -0
  68. data/lib/yard/server/router.rb +23 -0
  69. data/lib/yard/templates/helpers/base_helper.rb +22 -0
  70. data/lib/yard-lucid/version.rb +3 -0
  71. data/lib/yard-lucid.rb +36 -0
  72. data/yard-lucid.gemspec +38 -0
  73. metadata +215 -0
@@ -0,0 +1,307 @@
1
+ module Lucid
2
+ module Parser
3
+ class GherkinRepr < Gherkin::AstBuilder
4
+ # This class serves as the Gherkin representation for each feature file
5
+ # that is found. Specifically, the top level Feature object of each such
6
+ # file is given a representation. The Gherkin parser calls the various
7
+ # methods within in this class as it finds Gherkin-style elements.
8
+ #
9
+ # This process is similar to how most Gherkin tools generate an Abstract
10
+ # Syntax Tree. Here what gets generated are various YARD::CodeObjects.
11
+ #
12
+ # A namespace is specified and that is the place in the YARD namespacing
13
+ # where all features generated will reside. The namespace specified
14
+ # is the root namespace. This is the equivalent of the top-level
15
+ # directory holding all of the feature files.
16
+ def initialize(file)
17
+ super()
18
+ @namespace = YARD::CodeObjects::Lucid::LUCID_NAMESPACE
19
+ find_or_create_namespace(file)
20
+ @file = file
21
+ end
22
+
23
+ # This method returns the feature that has been defined. This is the
24
+ # final method that is called when all the work is done. What gets
25
+ # returned is the complete Feature object that was built.
26
+ def ast
27
+ feature(get_result) unless @feature
28
+ @feature
29
+ end
30
+
31
+ # Features that are found in sub-directories are considered to be in
32
+ # another namespace. The rationale is that with Gherkin-supporting test
33
+ # tools, when you execute a test run on a directory, any sub-directories
34
+ # of features will be executed with that directory.
35
+ #
36
+ # Part of the process involves the discovery of a README.md file within
37
+ # the specified directory of the feature file and loads that file as the
38
+ # description for the namespace. This is useful if you want to give a
39
+ # particular directory some supporting documentation.
40
+ def find_or_create_namespace(file)
41
+ @namespace = YARD::CodeObjects::Lucid::LUCID_NAMESPACE
42
+
43
+ File.dirname(file).split('/').each do |directory|
44
+ @namespace = @namespace.children.find { |child| child.is_a?(YARD::CodeObjects::Lucid::FeatureDirectory) && child.name.to_s == directory } ||
45
+ @namespace = YARD::CodeObjects::Lucid::FeatureDirectory.new(@namespace,directory) { |dir| dir.add_file(directory) }
46
+ end
47
+
48
+ if @namespace.description == "" && File.exists?("#{File.dirname(file)}/README.md")
49
+ @namespace.description = File.read("#{File.dirname(file)}/README.md")
50
+ end
51
+ end
52
+
53
+ # A given tag can be searched for, within the YARD Registry, to see if it
54
+ # exists and, if it doesn't, to create it. The logic will note that the
55
+ # tag was used in the given file at whatever the current line is and then
56
+ # add the tag to the current scenario or feature. It's also necessary to
57
+ # add the feature or scenario to the tag.
58
+ def find_or_create_tag(tag_name, parent)
59
+ tag_code_object = YARD::Registry.all(:tag).find { |tag| tag.value == tag_name } ||
60
+ YARD::CodeObjects::Lucid::Tag.new(YARD::CodeObjects::Lucid::LUCID_TAG_NAMESPACE,tag_name.gsub('@','')) { |t| t.owners = [] ; t.value = tag_name }
61
+
62
+ tag_code_object.add_file(@file,parent.line)
63
+
64
+ parent.tags << tag_code_object unless parent.tags.find { |tag| tag == tag_code_object }
65
+ tag_code_object.owners << parent unless tag_code_object.owners.find { |owner| owner == parent }
66
+ end
67
+
68
+ # Each feature found will call this method, generating the feature object.
69
+ # This happens only once, as the Gherkin parser does not allow for
70
+ # multiple features per feature file.
71
+ def feature(document)
72
+ #log.debug "FEATURE"
73
+ feature = document[:feature]
74
+ return unless document[:feature]
75
+ return if has_exclude_tags?(feature[:tags].map { |t| t[:name].gsub(/^@/, '') })
76
+
77
+ @feature = YARD::CodeObjects::Lucid::Feature.new(@namespace,File.basename(@file.gsub('.feature','').gsub('.','_'))) do |f|
78
+ f.comments = feature[:comments] ? feature[:comments].map{|comment| comment[:text]}.join("\n") : ''
79
+ f.description = feature[:description] || ''
80
+ f.add_file(@file,feature[:location][:line])
81
+ f.keyword = feature[:keyword]
82
+ f.value = feature[:name]
83
+ f.tags = []
84
+
85
+ feature[:tags].each {|feature_tag| find_or_create_tag(feature_tag[:name],f) }
86
+ end
87
+ feature[:children].each { |s|
88
+ case s[:type]
89
+ when :Background
90
+ background(s)
91
+ when :ScenarioOutline
92
+ scenario_outline(s)
93
+ when :Scenario
94
+ scenario(s)
95
+ end
96
+ }
97
+ end
98
+
99
+ # Called when a Background has been found. Note that to Gherkin a
100
+ # Background is really just another type of Scenario. The difference is
101
+ # that backgrounds get special processing during execution.
102
+ def background(background)
103
+ @background = YARD::CodeObjects::Lucid::Scenario.new(@feature,"background") do |b|
104
+ b.comments = background[:comments] ? background[:comments].map{|comment| comment.value}.join("\n") : ''
105
+ b.description = background[:description] || ''
106
+ b.keyword = background[:keyword]
107
+ b.value = background[:name]
108
+ b.add_file(@file,background[:location][:line])
109
+ end
110
+
111
+ @feature.background = @background
112
+ @background.feature = @feature
113
+ @step_container = @background
114
+ background[:steps].each { |s| step(s) }
115
+ end
116
+
117
+ # Called when a scenario has been found. This will create a scenario
118
+ # object, assign the scenario object to the feature object (and also
119
+ # assigne the feature object to the scenario object), as well as find
120
+ # or create tags that are associated with the scenario.
121
+ #
122
+ # The scenario is set as a type called a @step_container. This means
123
+ # that any steps found before another scenario is defined belong to this
124
+ # scenario.
125
+ def scenario(statement)
126
+ return if has_exclude_tags?(statement[:tags].map { |t| t[:name].gsub(/^@/, '') })
127
+
128
+ scenario = YARD::CodeObjects::Lucid::Scenario.new(@feature,"scenario_#{@feature.scenarios.length + 1}") do |s|
129
+ s.comments = statement[:comments] ? statement[:comments].map{|comment| comment.value}.join("\n") : ''
130
+ s.description = statement[:description] || ''
131
+ s.add_file(@file,statement[:location][:line])
132
+ s.keyword = statement[:keyword]
133
+ s.value = statement[:name]
134
+
135
+ statement[:tags].each {|scenario_tag| find_or_create_tag(scenario_tag[:name],s) }
136
+ end
137
+
138
+ scenario.feature = @feature
139
+ @feature.scenarios << scenario
140
+ @step_container = scenario
141
+ statement[:steps].each { |s| step(s) }
142
+ end
143
+
144
+ # Called when a scenario outline is found. This is very similar to a
145
+ # scenario but, to Gherkin, the ScenarioOutline is still a distinct
146
+ # object. The reason for this is because it can contain multiple
147
+ # different example groups that can contain different values.
148
+ def scenario_outline(statement)
149
+ return if has_exclude_tags?(statement[:tags].map { |t| t[:name].gsub(/^@/, '') })
150
+
151
+ outline = YARD::CodeObjects::Lucid::ScenarioOutline.new(@feature,"scenario_#{@feature.scenarios.length + 1}") do |s|
152
+ s.comments = statement[:comments] ? statement[:comments].map{|comment| comment.value}.join("\n") : ''
153
+ s.description = statement[:description] || ''
154
+ s.add_file(@file,statement[:location][:line])
155
+ s.keyword = statement[:keyword]
156
+ s.value = statement[:name]
157
+
158
+ statement[:tags].each {|scenario_tag| find_or_create_tag(scenario_tag[:name],s) }
159
+ end
160
+
161
+ outline.feature = @feature
162
+ @feature.scenarios << outline
163
+ @step_container = outline
164
+ statement[:steps].each { |s| step(s) }
165
+ statement[:examples].each { |e| examples(e) }
166
+ end
167
+
168
+ # Examples for a scenario outline are found. From a parsing perspective,
169
+ # the logic differs here from how a Gherkin-supporting tool parses for
170
+ # execution. For the needs of being lucid, each of the examples are
171
+ # expanded out as individual scenarios and step definitions. This is done
172
+ # so that it is possible to ensure that all variations of the scenario
173
+ # outline defined are displayed.
174
+ def examples(examples)
175
+ example = YARD::CodeObjects::Lucid::ScenarioOutline::Examples.new(:keyword => examples[:keyword],
176
+ :name => examples[:name],
177
+ :line => examples[:location][:line],
178
+ :comments => examples[:comments] ? examples.comments.map{|comment| comment.value}.join("\n") : '',
179
+ :rows => []
180
+ )
181
+ example.rows = [examples[:tableHeader][:cells].map{ |c| c[:value] }] if examples[:tableHeader]
182
+ example.rows += matrix(examples[:tableBody]) if examples[:tableBody]
183
+
184
+ @step_container.examples << example
185
+
186
+ # For each example data row, a new scenario must be generated using the
187
+ # current scenario as the template.
188
+
189
+ example.data.length.times do |row_index|
190
+
191
+ # Generate a copy of the scenario.
192
+
193
+ scenario = YARD::CodeObjects::Lucid::Scenario.new(@step_container,"example_#{@step_container.scenarios.length + 1}") do |s|
194
+ s.comments = @step_container.comments
195
+ s.description = @step_container.description
196
+ s.add_file(@file,@step_container.line_number)
197
+ s.keyword = @step_container.keyword
198
+ s.value = "#{@step_container.value} (#{@step_container.scenarios.length + 1})"
199
+ end
200
+
201
+ # Generate a copy of the scenario steps.
202
+
203
+ @step_container.steps.each do |step|
204
+ step_instance = YARD::CodeObjects::Lucid::Step.new(scenario,step.line_number) do |s|
205
+ s.keyword = step.keyword.dup
206
+ s.value = step.value.dup
207
+ s.add_file(@file,step.line_number)
208
+
209
+ s.text = step.text.dup if step.has_text?
210
+ s.table = clone_table(step.table) if step.has_table?
211
+ end
212
+
213
+ # Look at the particular data for the example row and do a simple
214
+ # find and replace of the <key> with the associated values. It's
215
+ # necessary ot handle empty cells in an example table.
216
+
217
+ example.values_for_row(row_index).each do |key,text|
218
+ text ||= ""
219
+ step_instance.value.gsub!("<#{key}>",text)
220
+ step_instance.text.gsub!("<#{key}>",text) if step_instance.has_text?
221
+ step_instance.table.each{ |row| row.each { |col| col.gsub!("<#{key}>",text) } } if step_instance.has_table?
222
+ end
223
+
224
+ # Connect the steps that have been created to the scenario that was
225
+ # created and then add the steps to the scenario.
226
+
227
+ step_instance.scenario = scenario
228
+ scenario.steps << step_instance
229
+ end
230
+
231
+ # Add the scenario to the list of scenarios maintained by the feature
232
+ # and add the feature to the scenario.
233
+
234
+ scenario.feature = @feature
235
+ @step_container.scenarios << scenario
236
+ end
237
+ end
238
+
239
+ # Called when a step is found. The logic here is that each step is
240
+ # referred to a table owner. This is the case even though not all steps
241
+ # have a table or multliline arguments associated with them.
242
+ #
243
+ # If a multiline string is present with the step it is included as the
244
+ # text of the step. If the step has a table it is added to the step using
245
+ # the same method used by the standard Gherkin model.
246
+ def step(step)
247
+ @table_owner = YARD::CodeObjects::Lucid::Step.new(@step_container,"#{step[:location][:line]}") do |s|
248
+ s.keyword = step[:keyword]
249
+ s.value = step[:text]
250
+ s.add_file(@file,step[:location][:line])
251
+ end
252
+
253
+ @table_owner.comments = step[:comments] ? step[:comments].map{|comment| comment.value}.join("\n") : ''
254
+
255
+ multiline_arg = step[:argument]
256
+
257
+ case(multiline_arg[:type])
258
+ when :DocString
259
+ @table_owner.text = multiline_arg[:content]
260
+ when :DataTable
261
+ @table_owner.table = matrix(multiline_arg[:rows])
262
+ end if multiline_arg
263
+
264
+ @table_owner.scenario = @step_container
265
+ @step_container.steps << @table_owner
266
+ end
267
+
268
+ # This is necessary because it is defined in many of the tooling that
269
+ # supports Gherkin, but there are no events for the end-of-file.
270
+ def eof
271
+ end
272
+
273
+ # This method exists when there is a syntax error. That matters for
274
+ # Gherkin execution but not for the parsing being done here.
275
+ def syntax_error(state, event, legal_events, line)
276
+ end
277
+
278
+ private
279
+
280
+ def matrix(gherkin_table)
281
+ gherkin_table.map {|gherkin_row| gherkin_row[:cells].map{ |cell| cell[:value] } }
282
+ end
283
+
284
+ # This helper method is used to deteremine what class is the current
285
+ # Gherkin class.
286
+ def gherkin_multiline_string_class
287
+ if defined?(Gherkin::Formatter::Model::PyString)
288
+ Gherkin::Formatter::Model::PyString
289
+ elsif defined?(Gherkin::Formatter::Model::DocString)
290
+ Gherkin::Formatter::Model::DocString
291
+ else
292
+ raise "Unable to find a suitable class in the Gherkin Library to parse the multiline step data."
293
+ end
294
+ end
295
+
296
+ def clone_table(base)
297
+ base.map {|row| row.map {|cell| cell.dup }}
298
+ end
299
+
300
+ def has_exclude_tags?(tags)
301
+ if YARD::Config.options["yard-lucid"] and YARD::Config.options["yard-lucid"]["exclude_tags"]
302
+ return true unless (YARD::Config.options["yard-lucid"]["exclude_tags"] & tags).empty?
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end
@@ -0,0 +1,3 @@
1
+ <div class="text">
2
+ <%= htmlify_with_newlines @step.text %>
3
+ </div>
@@ -0,0 +1,39 @@
1
+ <script type="text/javascript" charset="utf-8">
2
+ $(function() {
3
+ $(".developer").hide();
4
+ $(".scenario .title").css('cursor','pointer');
5
+ });
6
+ </script>
7
+
8
+ <div class="feature">
9
+ <div class="title">
10
+ <a id="view" class="control" href="#">[More Detail]</a>
11
+ <a id="expand" class="control" href="#">[Collapse All]</a>
12
+ <div style="clear: right;"></div>
13
+ <span class="pre"><%= @feature.keyword %>:</span>
14
+ <span class="name"><%= @feature.value %></span>
15
+ </div>
16
+
17
+ <% if @feature.comments.length > 0 %>
18
+ <div class="comments developer">
19
+ <%= htmlify_with_newlines @feature.comments %>
20
+ </div>
21
+ <% end %>
22
+
23
+ <div class="description">
24
+ <%= htmlify_with_newlines @feature.description %>
25
+ </div>
26
+
27
+ <div class="meta">
28
+ <div class="file developer"><%= h(@feature.file) %></div>
29
+ <div style="clear: right;"></div>
30
+ <div class="tags developer">
31
+ <% @feature.tags.each do |tag| %>
32
+ <a href="<%= url_for tag %>"><%= tag.value %></a>
33
+ <% end %>
34
+ </div>
35
+ </div>
36
+
37
+ <%= yieldall %>
38
+ </div>
39
+
@@ -0,0 +1 @@
1
+ <div class="none">No Steps Defined</div>
@@ -0,0 +1,52 @@
1
+ <% sc = 0 %>
2
+ <% @scenario.examples.each_with_index do |example, example_index| %>
3
+ <% example.data.each_with_index do |row, row_index| %>
4
+ <div style="display: none;" class="steps <%= "example#{example_index + 1}-#{row_index +1}" %>">
5
+ <% scenario = @scenario.scenarios[sc] %>
6
+ <% @scenario_outline = @scenario ; @scenario = scenario ; @steps = scenario.steps %>
7
+ <%= erb(:steps) %>
8
+ <% @scenario = @scenario_outline %>
9
+ <% sc += 1 %>
10
+ </div>
11
+ <% end %>
12
+ <% end %>
13
+
14
+ <div class="outline">
15
+ <% if @scenario.examples? %>
16
+ <% @scenario.examples.each_with_index do |example,example_index| %>
17
+ <div class="keyword">
18
+ <%= h example.keyword %><%= example.name != "" ? ':' : '' %>
19
+ </div>
20
+ <div class="example-group-name"><%= h example.name %></div>
21
+ <br style="clear: both;"/>
22
+ <table>
23
+ <thead>
24
+ <tr>
25
+ <% example.headers.each_with_index do |header,header_index| %>
26
+ <th><%= h(header) %></th>
27
+ <% end %>
28
+ </tr>
29
+ </thead>
30
+ <% unless example.data.empty? %>
31
+ <% example.data.each_with_index do |row,row_index| %>
32
+ <tr class="<%= (row_index + 1) % 2 == 0 ? "even example#{example_index+1}-#{row_index +1}" : "odd example#{example_index+1}-#{row_index +1}" %>">
33
+ <% row.each_with_index do |column,column_index| %>
34
+ <td><%= h(column.to_s.strip) %></td>
35
+ <% end %>
36
+ </tr>
37
+ <% end %>
38
+ <% else %>
39
+ <!-- Scenario Outline example table is empty -->
40
+ <tr class="odd">
41
+ <td colspan="<%= example.headers.length %>" style="text-align: center;">
42
+ No Examples Defined
43
+ </td>
44
+ </tr>
45
+ <% end %>
46
+ </table>
47
+ <% end %>
48
+ <% else %>
49
+ <div class="keyword">No Example Table Defined</div>
50
+ <div class="keyword suggestion developer">[!] Did you mean to create a Scenario?</div>
51
+ <% end %>
52
+ </div>
@@ -0,0 +1,52 @@
1
+ <div class="scenario <%= @id %>">
2
+ <a name="<%= @id %>" />
3
+ <div class="title">
4
+ <div style="float: left;">
5
+ <a class="toggle"> - </a>
6
+ <span class="pre"><%= @scenario.keyword %>:</span>
7
+ <span class="name"><%= h @scenario.value %></span>
8
+ </div>
9
+ <a class="link" style="float:right; clear:right;" href="<%= url_for(@scenario.feature, @id) %>">link</a>
10
+ </div>
11
+
12
+ <% if @scenario.description.length > 0 %>
13
+ <div class="description">
14
+ <%= htmlify_with_newlines @scenario.description %>
15
+ </div>
16
+ <% end %>
17
+
18
+ <% if @scenario.comments.length > 0 %>
19
+ <div class="comments developer">
20
+ <%= htmlify_with_newlines @scenario.comments %>
21
+ </div>
22
+ <% end %>
23
+
24
+ <div class="details">
25
+ <div class="meta developer">
26
+ <div class="file"><%= @scenario.location %></div>
27
+ <% unless @scenario.tags.empty? %>
28
+ <div style="clear:right;"></div>
29
+ <div class="tags">
30
+ <% @scenario.tags.each do |tag| %>
31
+ <a href="<%= url_for tag %>"><%= tag.value %></a>
32
+ <% end %>
33
+ </div>
34
+ <% end%>
35
+ <div style="clear: both;"></div>
36
+ </div>
37
+
38
+ <div class="steps">
39
+ <% if @scenario.steps.empty? %>
40
+ <%= erb(:no_steps_defined) %>
41
+ <% else %>
42
+ <%= @steps = @scenario.steps ; erb(:steps) %>
43
+ <% end %>
44
+ </div>
45
+
46
+ <%= erb(:outline) if @scenario.outline? %>
47
+ </div>
48
+
49
+ <div class="attributes" style="display:none;">
50
+ <input type="hidden" name="collapsed" value="false">
51
+ </div>
52
+ </div>
@@ -0,0 +1,51 @@
1
+ def init
2
+ super
3
+ @feature = object
4
+ sections.push :feature
5
+ sections.push :scenarios if object.scenarios
6
+ end
7
+
8
+ def background
9
+ @scenario = @feature.background
10
+ @id = "background"
11
+ erb(:scenario)
12
+ end
13
+
14
+ def scenarios
15
+ scenarios = ""
16
+
17
+ if @feature.background
18
+ @scenario = @feature.background
19
+ @id = "background"
20
+ scenarios += erb(:scenario)
21
+ end
22
+
23
+ @feature.scenarios.each_with_index do |scenario,index|
24
+ @scenario = scenario
25
+ @id = "scenario_#{index}"
26
+ scenarios += erb(:scenario)
27
+ end
28
+
29
+ scenarios
30
+ end
31
+
32
+ def highlight_matches(step)
33
+ value = step.value.dup
34
+
35
+ if step.definition
36
+ matches = step.value.match(step.definition.regex)
37
+
38
+ if matches
39
+ matches[1..-1].reverse.each_with_index do |match,index|
40
+ next if match == nil
41
+ value[matches.begin((matches.size - 1) - index)..(matches.end((matches.size - 1) - index) - 1)] = "<span class='match'>#{h(match)}</span>"
42
+ end
43
+ end
44
+ end
45
+
46
+ value
47
+ end
48
+
49
+ def htmlify_with_newlines(text)
50
+ text.split("\n").collect {|c| h(c).gsub(/\s/,'&nbsp;') }.join("<br/>")
51
+ end
@@ -0,0 +1,37 @@
1
+ <% @steps.each_with_index do |step,index| %>
2
+ <% @step = step %>
3
+
4
+ <% if step.comments && step.comments.length > 0 %>
5
+ <div class="comments developer"><%= htmlify_with_newlines step.comments %></div>
6
+ <% end %>
7
+ <div class="step <%= (index + 1) % 2 == 0 ? 'even' : 'odd' %>">
8
+ <span class="predicate"><%= step.keyword %></span>
9
+
10
+ <% if @scenario.outline? %>
11
+ <%= h step.value %>
12
+ <% else %>
13
+
14
+ <% if step.definition %>
15
+ <span class="defined">
16
+ <%= highlight_matches(step) %>
17
+ <div class="definition developer">
18
+ <a href="<%= url_for step.definition %>"><div class="valid">&nbsp;</div></a>
19
+ </div>
20
+ </span>
21
+ <% else %>
22
+ <span class="undefined">
23
+ <%= h step.value %>
24
+ <div class="definition developer">
25
+ <a href="<%= url_for YARD::CodeObjects::Lucid::LUCID_STEPTRANSFORM_NAMESPACE %>#undefined_steps">
26
+ <div class="invalid">&nbsp;</div>
27
+ </a>
28
+ </div>
29
+ </span>
30
+ <% end %>
31
+
32
+ <% end %>
33
+ </div>
34
+
35
+ <%= erb(:table) if step.has_table? %>
36
+ <%= erb(:docstring) if step.has_text? %>
37
+ <% end %>
@@ -0,0 +1,19 @@
1
+ <div class="multiline">
2
+ <table style="">
3
+ <thead>
4
+ <tr>
5
+ <% @step.table.first.each_with_index do |column, column_index| %>
6
+ <th class="<%= (column_index + 1) % 2 == 0 ? 'even' : 'odd' %>"><%= h(column.strip) %></th>
7
+ <% end %>
8
+ </tr>
9
+ </thead>
10
+
11
+ <% @step.table[1..-1].each_with_index do |row, row_index| %>
12
+ <tr class="<%= (row_index + 1) % 2 == 0 ? 'even' : 'odd' %>">
13
+ <% row.each_with_index do |column, column_index| %>
14
+ <td><%= h(column.strip) %></td>
15
+ <% end %>
16
+ </tr>
17
+ <% end %>
18
+ </table>
19
+ </div>
@@ -0,0 +1,30 @@
1
+ <% if @elements && !@elements.empty? %>
2
+ <% i = (@elements.length % 2 == 0 ? 1 : 0) %>
3
+ <table style="margin-left: 10px; width: 100%;">
4
+ <tr>
5
+ <td valign='top' width="50%">
6
+ <% @elements.each do |letter, objects| %>
7
+ <% if (i += 1) > (@elements.length / 2 + 1) %>
8
+ </td><td valign='top' width="50%">
9
+ <% i = 0 %>
10
+ <% end %>
11
+ <ul id="alpha_<%= letter %>" class="alpha">
12
+ <li class="letter"><%= letter %></li>
13
+ <ul>
14
+ <% objects.each do |obj| %>
15
+ <li>
16
+ <%= linkify obj, obj.value %>
17
+ <% if obj.is_a? YARD::CodeObjects::Lucid::FeatureDirectory %>
18
+ <small>(<%= obj.expanded_path %>)</small>
19
+ <% else %>
20
+ <small>(<%= obj.file %>)</small>
21
+ <% end %>
22
+ </li>
23
+ <% end %>
24
+ </ul>
25
+ </ul>
26
+ <% end %>
27
+ </td>
28
+ </tr>
29
+ </table>
30
+ <% end %>
@@ -0,0 +1,30 @@
1
+ <% if @directory %>
2
+ <div class="specifications">
3
+ <div class="title">
4
+ <span class="pre">Directory:</span>
5
+ <span class="name"><%= h @directory.name.to_s.capitalize %></span>
6
+ </div>
7
+
8
+ <div class="readme">
9
+ <%= markdown @directory.description %>
10
+ </div>
11
+
12
+ <% if features && !features.empty? %>
13
+ <%= alpha_table(features) %>
14
+ <% end %>
15
+
16
+ <div id="directory">
17
+ <div class="title"><span class="name">Tags</span></div>
18
+ </div>
19
+ <div class="tags">
20
+ <%= tags.collect {|tag| linkify(tag,tag.value) }.join(",\n") %>
21
+ </div>
22
+
23
+ <% if directories && !directories.empty? %>
24
+ <div id="directory">
25
+ <div class="title"><span class="name">Subdirectories</span></div>
26
+ </div>
27
+ <%= alpha_table(directories) %>
28
+ <% end %>
29
+ </div>
30
+ <% end %>
@@ -0,0 +1,41 @@
1
+ def init
2
+ super
3
+ sections.push :directory
4
+ @directory = object
5
+ end
6
+
7
+ def markdown(text)
8
+ htmlify(text,:markdown) rescue h(text)
9
+ end
10
+
11
+ def htmlify_with_newlines(text)
12
+ text.split("\n").collect { |c| h(c).gsub(/\s/,'&nbsp;') }.join("<br/>")
13
+ end
14
+
15
+ def directories
16
+ @directories ||= @directory.subdirectories
17
+ end
18
+
19
+ def features
20
+ @features ||= @directory.features + directories.collect { |d| d.features }.flatten
21
+ end
22
+
23
+ def scenarios
24
+ @scenarios ||= features.collect { |feature| feature.scenarios }.flatten
25
+ end
26
+
27
+ def tags
28
+ @tags ||= (features.collect { |feature| feature.tags } + scenarios.collect { |scenario| scenario.tags }).flatten.uniq.sort_by { |l| l.value.to_s }
29
+ end
30
+
31
+ def alpha_table(objects)
32
+ @elements = Hash.new
33
+
34
+ objects = run_verifier(objects)
35
+ objects.each { |o| (@elements[o.value.to_s[0,1].upcase] ||= []) << o }
36
+ @elements.values.each { |v| v.sort! {|a,b| b.value.to_s <=> a.value.to_s } }
37
+ @elements = @elements.sort_by { |l,o| l.to_s }
38
+
39
+ @elements.each {|letter,objects| objects.sort! { |a,b| b.value.to_s <=> a.value.to_s } }
40
+ erb(:alpha_table)
41
+ end