talk 2.0.1 → 2.0.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.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.travis.yml +6 -0
- data/Gemfile +10 -0
- data/README.md +6 -0
- data/Rakefile +9 -0
- data/features/class-field.feature +226 -0
- data/features/class.feature +95 -0
- data/features/enumeration-constant.feature +76 -0
- data/features/enumeration.feature +35 -0
- data/features/glossary-term.feature +46 -0
- data/features/glossary.feature +35 -0
- data/features/step_definitions/class-field.rb +74 -0
- data/features/step_definitions/class.rb +50 -0
- data/features/step_definitions/enumeration-constant.rb +42 -0
- data/features/step_definitions/enumeration.rb +29 -0
- data/features/step_definitions/glossary-term.rb +31 -0
- data/features/step_definitions/glossary.rb +23 -0
- data/features/support/env.rb +261 -0
- data/lib/context.rb +282 -0
- data/lib/context_class.rb +224 -0
- data/lib/contexts/README.md +274 -0
- data/lib/contexts/base.rb +6 -0
- data/lib/contexts/boolean.rb +5 -0
- data/lib/contexts/class.rb +11 -0
- data/lib/contexts/constant.rb +5 -0
- data/lib/contexts/enumeration.rb +20 -0
- data/lib/contexts/field.rb +47 -0
- data/lib/contexts/glossary.rb +7 -0
- data/lib/contexts/inherits.rb +3 -0
- data/lib/contexts/map.rb +7 -0
- data/lib/contexts/meta.rb +2 -0
- data/lib/contexts/method.rb +15 -0
- data/lib/contexts/numeric.rb +5 -0
- data/lib/contexts/protocol.rb +8 -0
- data/lib/contexts/reference.rb +6 -0
- data/lib/contexts/string.rb +5 -0
- data/lib/contexts/target.rb +11 -0
- data/lib/contexts/term.rb +11 -0
- data/lib/languages/java/java.rb +145 -0
- data/lib/languages/java/templates/class.java.erb +22 -0
- data/lib/languages/java/templates/enumeration.java.erb +10 -0
- data/lib/languages/java/templates/glossary.java.erb +8 -0
- data/lib/languages/language.rb +172 -0
- data/lib/languages/objc/objc.rb +162 -0
- data/lib/languages/objc/templates/TalkClasses.h.erb +3 -0
- data/lib/languages/objc/templates/TalkClassesForward.h.erb +2 -0
- data/lib/languages/objc/templates/TalkConstants.h.erb +22 -0
- data/lib/languages/objc/templates/TalkObjectList.h.erb +4 -0
- data/lib/languages/objc/templates/class.h.erb +21 -0
- data/lib/languages/objc/templates/class.m.erb +43 -0
- data/lib/parse_error.rb +4 -0
- data/lib/parser.rb +119 -0
- data/lib/registry.rb +151 -0
- data/lib/talk.rb +5 -0
- data/talk.gemspec +18 -0
- metadata +71 -3
@@ -0,0 +1,22 @@
|
|
1
|
+
<%= autogenerated_warning %>
|
2
|
+
<% @base[:glossary].each do |glossary| %>
|
3
|
+
|
4
|
+
// @glossary <%= glossary[:name] %>
|
5
|
+
<%= comment_block(glossary) %>
|
6
|
+
|
7
|
+
<% glossary[:term].each do |term| %>
|
8
|
+
<%= comment_block(term) %>
|
9
|
+
#define <%= glossary_term_name(term[:name]) %> @"<%= term[:value] %>"
|
10
|
+
<% end # term %>
|
11
|
+
|
12
|
+
<% end # glossary %>
|
13
|
+
|
14
|
+
<% @base[:enumeration].each do |enumeration| %>
|
15
|
+
|
16
|
+
<%= comment_block(enumeration) %>
|
17
|
+
enum <%= truncated_name(enumeration[:name]) %>
|
18
|
+
{
|
19
|
+
<%= (enumeration[:constant].map { |constant| "\t"+constant_definition(constant) }).join("\n") %>
|
20
|
+
};
|
21
|
+
|
22
|
+
<% end # enumeration %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<%= autogenerated_warning %>
|
2
|
+
#import "TalkObjectList.h"
|
3
|
+
|
4
|
+
<%= comment_block(@current_class) %>
|
5
|
+
@interface <%= "#{truncated_name(@current_class)}" %> : <%= "#{superclass(@current_class)}" %>
|
6
|
+
{
|
7
|
+
<%=
|
8
|
+
lines = []
|
9
|
+
@current_class[:field].each do |field|
|
10
|
+
lines.push comment_block(field, 1)
|
11
|
+
lines.push "\t"+field_definition(@current_class, field) + "; // " + field[:type].join('')
|
12
|
+
lines.push ""
|
13
|
+
end
|
14
|
+
|
15
|
+
lines.join("\n")
|
16
|
+
%>}
|
17
|
+
|
18
|
+
<%= (@current_class[:field].map { |f| "@property #{field_tags(f)} #{field_definition(@current_class, f)};" }).join("\n") %>
|
19
|
+
+(NSString *) fullyQualifiedClassName;
|
20
|
+
|
21
|
+
@end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<%= autogenerated_warning %>
|
2
|
+
#define __CANONFILE__ "<%= truncated_name(@current_class) %>"
|
3
|
+
#import "TalkObjectList.h"
|
4
|
+
#import "TalkObject+Private.h"
|
5
|
+
|
6
|
+
#if ! __has_feature(objc_arc)
|
7
|
+
#error This file requires Automatic Reference Counting
|
8
|
+
#endif
|
9
|
+
|
10
|
+
@implementation <%= truncated_name(@current_class) %>
|
11
|
+
<%= dynamic_body_for_named_wrapper %>
|
12
|
+
<% unless trimmed_fields(@current_class).empty? then %>
|
13
|
+
@synthesize <%= (trimmed_fields(@current_class).map { |f| mapped_name(@current_class, f[:name], :field) }).join(', ') %>;
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
-(void) initKeymaps
|
17
|
+
{
|
18
|
+
[super initKeymaps];
|
19
|
+
<% @target[:map].each do |map|
|
20
|
+
next unless map[:class_name] == truncated_name(@current_class[:name])%>
|
21
|
+
[self mapSerializedKey:@"<%= map[:field_name] %>" toLocalKey:@"<%= map[:new_field_name] %>"];
|
22
|
+
<% end %>}
|
23
|
+
|
24
|
+
<% unless (@current_class[:field].reject { |f| assist_line(f).nil? }).length == 0 then %>
|
25
|
+
-(NSString *) containerStructureForLocalKey: (NSString *) localKey
|
26
|
+
{<%
|
27
|
+
@current_class[:field].each do |field|
|
28
|
+
assist = assist_line(field)
|
29
|
+
unless assist.nil? %>
|
30
|
+
if([localKey isEqualToString:@"<%= mapped_name(@current_class, field[:name], :field) %>"]) return @"<%= assist %>";
|
31
|
+
<%
|
32
|
+
end
|
33
|
+
end
|
34
|
+
%>
|
35
|
+
return [super containerStructureForLocalKey:localKey];
|
36
|
+
}
|
37
|
+
<% end %>
|
38
|
+
+(NSString *) fullyQualifiedClassName
|
39
|
+
{
|
40
|
+
return @"<%= @current_class[:name] %>";
|
41
|
+
}
|
42
|
+
|
43
|
+
@end
|
data/lib/parse_error.rb
ADDED
data/lib/parser.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require "parse_error.rb"
|
2
|
+
require "context"
|
3
|
+
|
4
|
+
module Talk
|
5
|
+
attr_reader :contexts, :finalized
|
6
|
+
|
7
|
+
@contexts = {}
|
8
|
+
|
9
|
+
class Parser
|
10
|
+
attr_accessor :basepath
|
11
|
+
|
12
|
+
def self.error(tag, file, line, message)
|
13
|
+
near_msg = tag.nil? ? "" : " near @#{tag}"
|
14
|
+
raise ParseError, "#{file}:#{line} parse error#{near_msg}: #{message}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@contexts = [ Context.context_for_name(:base).new("base", "n/a", "0") ]
|
19
|
+
@base = @contexts.first
|
20
|
+
@finalized = false
|
21
|
+
@closed_contexts = []
|
22
|
+
end
|
23
|
+
|
24
|
+
# signal that we are done parsing files, and it is time to do final validation
|
25
|
+
def finalize
|
26
|
+
close_active_context until @contexts.empty?
|
27
|
+
@closed_contexts.each { |ctx| ctx.finalize }
|
28
|
+
finalized = true
|
29
|
+
end
|
30
|
+
|
31
|
+
def results
|
32
|
+
finalize unless @finalized
|
33
|
+
@base.to_h
|
34
|
+
end
|
35
|
+
|
36
|
+
def trim_filename(filename)
|
37
|
+
if @basepath.nil? == false and filename.start_with? @basepath then
|
38
|
+
filename = filename[@basepath.length .. -1]
|
39
|
+
filename = filename[1..-1] while filename.start_with? "/"
|
40
|
+
end
|
41
|
+
|
42
|
+
filename
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_file(filename)
|
46
|
+
parse(filename, IO.read(filename))
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse(filename, contents)
|
50
|
+
contents = contents.split("\n") unless contents.is_a? Array
|
51
|
+
contents.each_with_index { |line, line_num| parse_line(line.strip.split, filename, line_num+1) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_line(words, file, line)
|
55
|
+
return if line_is_comment?(words)
|
56
|
+
|
57
|
+
@file = trim_filename(file)
|
58
|
+
@line = line
|
59
|
+
words.each { |word| parse_word(word) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def parse_word(word)
|
63
|
+
@word = word
|
64
|
+
|
65
|
+
if word_is_tag?(word) then
|
66
|
+
parse_tag(identifier_from_tag_word(word))
|
67
|
+
else
|
68
|
+
@contexts.last.parse(word, @file, @line)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_tag(tag)
|
73
|
+
@tag = tag
|
74
|
+
@contexts.last.has_tag?(tag) ? parse_supported_tag : parse_unsupported_tag
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse_supported_tag
|
78
|
+
new_context = @contexts.last.start_tag(@tag, @file, @line)
|
79
|
+
if new_context.nil? then
|
80
|
+
close_active_context
|
81
|
+
else
|
82
|
+
@contexts.push new_context
|
83
|
+
@closed_contexts.push new_context
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def parse_unsupported_tag
|
88
|
+
stack = Array.new(@contexts)
|
89
|
+
stack.pop until stack.empty? or stack.last.has_tag? @tag
|
90
|
+
|
91
|
+
parse_error("tag not supported in @#{@contexts.last.tag.to_s}") if stack.empty?
|
92
|
+
|
93
|
+
close_active_context until @contexts.last == stack.last
|
94
|
+
|
95
|
+
parse_supported_tag
|
96
|
+
end
|
97
|
+
|
98
|
+
def close_active_context
|
99
|
+
@closed_contexts.push @contexts.pop
|
100
|
+
@contexts.last.end_tag(@closed_contexts.last) unless @contexts.empty?
|
101
|
+
end
|
102
|
+
|
103
|
+
def word_is_tag?(word)
|
104
|
+
word[0] == '@'
|
105
|
+
end
|
106
|
+
|
107
|
+
def identifier_from_tag_word(word)
|
108
|
+
word[1..-1].to_sym
|
109
|
+
end
|
110
|
+
|
111
|
+
def line_is_comment?(line)
|
112
|
+
line.length > 0 and line[0].length > 0 and line[0][0] == '#'
|
113
|
+
end
|
114
|
+
|
115
|
+
def parse_error(message)
|
116
|
+
raise Talk::Parser.error(@tag, @file, @line, message)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/lib/registry.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
class Array
|
2
|
+
def each_prefix
|
3
|
+
self.length.times do |i|
|
4
|
+
yield(self[0..i])
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Talk
|
10
|
+
class RegistryEntry
|
11
|
+
attr_reader :file, :line, :name
|
12
|
+
|
13
|
+
def initialize(name=nil, file=nil, line=nil)
|
14
|
+
@file = file
|
15
|
+
@line = line
|
16
|
+
@name = name
|
17
|
+
@children = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def make_entry(name, file, line)
|
21
|
+
@name = name
|
22
|
+
@file = file
|
23
|
+
@line = line
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](key)
|
27
|
+
@children[key]
|
28
|
+
end
|
29
|
+
|
30
|
+
def []=(key, value)
|
31
|
+
@children[key] = value
|
32
|
+
end
|
33
|
+
|
34
|
+
def keys
|
35
|
+
@children.keys
|
36
|
+
end
|
37
|
+
|
38
|
+
def each(&block)
|
39
|
+
@children.each &block
|
40
|
+
end
|
41
|
+
|
42
|
+
def has_children?
|
43
|
+
not @children.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
def is_entry?
|
47
|
+
@file != nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
is_entry? ? "#{@file}:#{@line}" : "container"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Registry
|
56
|
+
class << self
|
57
|
+
def add(name, namespace, file, line, delimiter=nil)
|
58
|
+
if registered?(name, namespace) then
|
59
|
+
old_reg = get_registrations(name, namespace).last
|
60
|
+
Talk::Parser.error(nil, file, line, "Duplicate registration #{name} in #{namespace}; previously defined at #{old_reg}")
|
61
|
+
end
|
62
|
+
|
63
|
+
@registry ||= {}
|
64
|
+
@registry[namespace] ||= {}
|
65
|
+
split_name = delimiter.nil? ? [*name] : name.to_s.split(delimiter)
|
66
|
+
|
67
|
+
# create each level of the split name
|
68
|
+
level = @registry[namespace]
|
69
|
+
split_name[0..-2].each do |component| # all but the last component
|
70
|
+
level[component] ||= RegistryEntry.new
|
71
|
+
level = level[component]
|
72
|
+
end
|
73
|
+
|
74
|
+
level[split_name.last] ||= RegistryEntry.new(name, file, line)
|
75
|
+
level[split_name.last].make_entry(name, file, line) # in case it already existed as a container
|
76
|
+
add_reverse_lookup(split_name, namespace, level[split_name.last], delimiter)
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_reverse_lookup(split_name, namespace, entry, delimiter)
|
80
|
+
@reverse ||= {}
|
81
|
+
@reverse[namespace] ||= {}
|
82
|
+
split_name.reverse.each_prefix do |prefix|
|
83
|
+
name = prefix.reverse.join(delimiter)
|
84
|
+
@reverse[namespace][name] ||= []
|
85
|
+
@reverse[namespace][name].push entry
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_registrations(name, namespace, match_exact = false)
|
90
|
+
@registry ||= {}
|
91
|
+
exact = get_exact_registrations(name, namespace)
|
92
|
+
|
93
|
+
return exact unless exact.empty?
|
94
|
+
return [] if match_exact
|
95
|
+
|
96
|
+
get_inexact_registrations(name, namespace)
|
97
|
+
end
|
98
|
+
|
99
|
+
def get_exact_registrations(name, namespace)
|
100
|
+
level = @registry[namespace]
|
101
|
+
[*name].each do |component|
|
102
|
+
return [] if level.nil?
|
103
|
+
level = level[component]
|
104
|
+
end
|
105
|
+
|
106
|
+
return [*level] if not level.nil? and level.is_entry?
|
107
|
+
[]
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_inexact_registrations(name, namespace)
|
111
|
+
return [] if @reverse.nil? or @reverse[namespace].nil? or @reverse[namespace][name].nil?
|
112
|
+
@reverse[namespace][name]
|
113
|
+
end
|
114
|
+
|
115
|
+
def registered?(name, namespace, match_exact = false)
|
116
|
+
not get_registrations(name, namespace, match_exact).empty?
|
117
|
+
end
|
118
|
+
|
119
|
+
def render_level(level, at_depth=1)
|
120
|
+
indent = " "*at_depth
|
121
|
+
s = ""
|
122
|
+
level.keys.sort.each do |key|
|
123
|
+
value = level[key]
|
124
|
+
s += indent + key
|
125
|
+
s += " (entry from #{File.basename(value.file)}:#{value.line})" if value.is_entry?
|
126
|
+
s += "\n"
|
127
|
+
s += render_level(value, at_depth+1)
|
128
|
+
end
|
129
|
+
|
130
|
+
s
|
131
|
+
end
|
132
|
+
|
133
|
+
def reset
|
134
|
+
@registry = nil
|
135
|
+
@reverse = nil
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_s
|
139
|
+
return "Empty registry" if @registry.nil? or @registry.empty?
|
140
|
+
s = ""
|
141
|
+
@registry.keys.sort.each do |namespace|
|
142
|
+
level = @registry[namespace]
|
143
|
+
s += "Namespace #{namespace}:\n"
|
144
|
+
s += render_level(level)
|
145
|
+
end
|
146
|
+
|
147
|
+
s
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
data/lib/talk.rb
ADDED
data/talk.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'talk'
|
3
|
+
s.executables << 'talk'
|
4
|
+
s.version = '2.0.2'
|
5
|
+
s.date = '2014-05-14'
|
6
|
+
s.summary = "Compile-to-source protocol contract specification language"
|
7
|
+
s.description = "A lightweight language for specifying protocol contracts. Compiles to source in Java, Javascript, ObjC and Ruby."
|
8
|
+
s.authors = ["Jonas Acres"]
|
9
|
+
s.email = 'jonas@becuddle.com'
|
10
|
+
s.homepage = 'http://github.com/jonasacres/talk'
|
11
|
+
s.license = 'GPLv2'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split($/)
|
14
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
15
|
+
s.test_files = s.files.grep(%r{^(test|s|features)/})
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: talk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Acres
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-05-14 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A lightweight language for specifying protocol contracts. Compiles to
|
14
14
|
source in Java, Javascript, ObjC and Ruby.
|
@@ -18,7 +18,62 @@ executables:
|
|
18
18
|
extensions: []
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
|
+
- .gitignore
|
22
|
+
- .travis.yml
|
23
|
+
- Gemfile
|
24
|
+
- README.md
|
25
|
+
- Rakefile
|
21
26
|
- bin/talk
|
27
|
+
- features/class-field.feature
|
28
|
+
- features/class.feature
|
29
|
+
- features/enumeration-constant.feature
|
30
|
+
- features/enumeration.feature
|
31
|
+
- features/glossary-term.feature
|
32
|
+
- features/glossary.feature
|
33
|
+
- features/step_definitions/class-field.rb
|
34
|
+
- features/step_definitions/class.rb
|
35
|
+
- features/step_definitions/enumeration-constant.rb
|
36
|
+
- features/step_definitions/enumeration.rb
|
37
|
+
- features/step_definitions/glossary-term.rb
|
38
|
+
- features/step_definitions/glossary.rb
|
39
|
+
- features/support/env.rb
|
40
|
+
- lib/context.rb
|
41
|
+
- lib/context_class.rb
|
42
|
+
- lib/contexts/README.md
|
43
|
+
- lib/contexts/base.rb
|
44
|
+
- lib/contexts/boolean.rb
|
45
|
+
- lib/contexts/class.rb
|
46
|
+
- lib/contexts/constant.rb
|
47
|
+
- lib/contexts/enumeration.rb
|
48
|
+
- lib/contexts/field.rb
|
49
|
+
- lib/contexts/glossary.rb
|
50
|
+
- lib/contexts/inherits.rb
|
51
|
+
- lib/contexts/map.rb
|
52
|
+
- lib/contexts/meta.rb
|
53
|
+
- lib/contexts/method.rb
|
54
|
+
- lib/contexts/numeric.rb
|
55
|
+
- lib/contexts/protocol.rb
|
56
|
+
- lib/contexts/reference.rb
|
57
|
+
- lib/contexts/string.rb
|
58
|
+
- lib/contexts/target.rb
|
59
|
+
- lib/contexts/term.rb
|
60
|
+
- lib/languages/java/java.rb
|
61
|
+
- lib/languages/java/templates/class.java.erb
|
62
|
+
- lib/languages/java/templates/enumeration.java.erb
|
63
|
+
- lib/languages/java/templates/glossary.java.erb
|
64
|
+
- lib/languages/language.rb
|
65
|
+
- lib/languages/objc/objc.rb
|
66
|
+
- lib/languages/objc/templates/TalkClasses.h.erb
|
67
|
+
- lib/languages/objc/templates/TalkClassesForward.h.erb
|
68
|
+
- lib/languages/objc/templates/TalkConstants.h.erb
|
69
|
+
- lib/languages/objc/templates/TalkObjectList.h.erb
|
70
|
+
- lib/languages/objc/templates/class.h.erb
|
71
|
+
- lib/languages/objc/templates/class.m.erb
|
72
|
+
- lib/parse_error.rb
|
73
|
+
- lib/parser.rb
|
74
|
+
- lib/registry.rb
|
75
|
+
- lib/talk.rb
|
76
|
+
- talk.gemspec
|
22
77
|
homepage: http://github.com/jonasacres/talk
|
23
78
|
licenses:
|
24
79
|
- GPLv2
|
@@ -43,4 +98,17 @@ rubygems_version: 2.0.14
|
|
43
98
|
signing_key:
|
44
99
|
specification_version: 4
|
45
100
|
summary: Compile-to-source protocol contract specification language
|
46
|
-
test_files:
|
101
|
+
test_files:
|
102
|
+
- features/class-field.feature
|
103
|
+
- features/class.feature
|
104
|
+
- features/enumeration-constant.feature
|
105
|
+
- features/enumeration.feature
|
106
|
+
- features/glossary-term.feature
|
107
|
+
- features/glossary.feature
|
108
|
+
- features/step_definitions/class-field.rb
|
109
|
+
- features/step_definitions/class.rb
|
110
|
+
- features/step_definitions/enumeration-constant.rb
|
111
|
+
- features/step_definitions/enumeration.rb
|
112
|
+
- features/step_definitions/glossary-term.rb
|
113
|
+
- features/step_definitions/glossary.rb
|
114
|
+
- features/support/env.rb
|