expressir 2.1.17 → 2.1.19
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/.rubocop_todo.yml +17 -9
- data/README.adoc +225 -3
- data/expressir.gemspec +3 -0
- data/lib/expressir/cli.rb +28 -193
- data/lib/expressir/commands/base.rb +32 -0
- data/lib/expressir/commands/benchmark.rb +68 -0
- data/lib/expressir/commands/benchmark_cache.rb +88 -0
- data/lib/expressir/commands/clean.rb +20 -0
- data/lib/expressir/commands/coverage.rb +307 -0
- data/lib/expressir/commands/format.rb +13 -0
- data/lib/expressir/commands/validate.rb +53 -0
- data/lib/expressir/commands/version.rb +9 -0
- data/lib/expressir/coverage.rb +249 -0
- data/lib/expressir/express/formatter.rb +4 -2
- data/lib/expressir/express/parser.rb +9 -9
- data/lib/expressir/express/visitor.rb +12 -8
- data/lib/expressir/model/declarations/derived_attribute.rb +25 -0
- data/lib/expressir/model/declarations/inverse_attribute.rb +25 -0
- data/lib/expressir/model/model_element.rb +2 -0
- data/lib/expressir/version.rb +1 -1
- data/lib/expressir.rb +1 -0
- metadata +55 -3
- data/lib/expressir/express/express_remarks_decorator.rb +0 -54
@@ -0,0 +1,249 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module Expressir
|
4
|
+
# Coverage module for calculating documentation coverage of EXPRESS entities
|
5
|
+
module Coverage
|
6
|
+
# Mapping of EXPRESS entity type names to their corresponding class names
|
7
|
+
ENTITY_TYPE_MAP = {
|
8
|
+
"TYPE" => "Expressir::Model::Declarations::Type",
|
9
|
+
"ENTITY" => "Expressir::Model::Declarations::Entity",
|
10
|
+
"CONSTANT" => "Expressir::Model::Declarations::Constant",
|
11
|
+
"FUNCTION" => "Expressir::Model::Declarations::Function",
|
12
|
+
"RULE" => "Expressir::Model::Declarations::Rule",
|
13
|
+
"PROCEDURE" => "Expressir::Model::Declarations::Procedure",
|
14
|
+
"SUBTYPE_CONSTRAINT" => "Expressir::Model::Declarations::SubtypeConstraint",
|
15
|
+
}.freeze
|
16
|
+
# Represents a documentation coverage report for EXPRESS schemas
|
17
|
+
class Report
|
18
|
+
attr_reader :repository, :schema_reports, :total_entities, :documented_entities,
|
19
|
+
:undocumented_entities
|
20
|
+
|
21
|
+
# Initialize a coverage report
|
22
|
+
# @param repository [Expressir::Model::Repository] The repository to analyze
|
23
|
+
# @param skip_types [Array<String>] Array of entity type names to skip from coverage
|
24
|
+
def initialize(repository, skip_types = [])
|
25
|
+
@repository = repository
|
26
|
+
@skip_types = skip_types
|
27
|
+
@schema_reports = []
|
28
|
+
@total_entities = []
|
29
|
+
@documented_entities = []
|
30
|
+
@undocumented_entities = []
|
31
|
+
|
32
|
+
process_repository
|
33
|
+
end
|
34
|
+
|
35
|
+
# Create a report from a repository
|
36
|
+
# @param repository [Expressir::Model::Repository] The repository to analyze
|
37
|
+
# @param skip_types [Array<String>] Array of entity type names to skip from coverage
|
38
|
+
# @return [Report] The coverage report
|
39
|
+
def self.from_repository(repository, skip_types = [])
|
40
|
+
new(repository, skip_types)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Create a report from a schema file
|
44
|
+
# @param path [String] Path to the schema file
|
45
|
+
# @param skip_types [Array<String>] Array of entity type names to skip from coverage
|
46
|
+
# @return [Report] The coverage report
|
47
|
+
def self.from_file(path, skip_types = [])
|
48
|
+
repository = Expressir::Express::Parser.from_file(path)
|
49
|
+
new(repository, skip_types)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Calculate the overall coverage percentage
|
53
|
+
# @return [Float] The coverage percentage
|
54
|
+
def coverage_percentage
|
55
|
+
return 100.0 if @total_entities.empty?
|
56
|
+
|
57
|
+
(@documented_entities.size.to_f / @total_entities.size) * 100
|
58
|
+
end
|
59
|
+
|
60
|
+
# Get file-level reports
|
61
|
+
# @return [Array<Hash>] Array of file report hashes
|
62
|
+
def file_reports
|
63
|
+
@schema_reports.map do |report|
|
64
|
+
absolute_path = report[:schema].file
|
65
|
+
relative_path = begin
|
66
|
+
Pathname.new(absolute_path).relative_path_from(Pathname.pwd).to_s
|
67
|
+
rescue ArgumentError
|
68
|
+
# If paths are on different drives or otherwise incompatible,
|
69
|
+
# fall back to the absolute path
|
70
|
+
absolute_path
|
71
|
+
end
|
72
|
+
|
73
|
+
{
|
74
|
+
"file" => relative_path,
|
75
|
+
"file_basename" => File.basename(absolute_path),
|
76
|
+
"directory" => File.dirname(absolute_path),
|
77
|
+
"total" => report[:total].size,
|
78
|
+
"documented" => report[:documented].size,
|
79
|
+
"undocumented" => report[:undocumented],
|
80
|
+
"coverage" => report[:coverage],
|
81
|
+
}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get directory-level reports
|
86
|
+
# @return [Array<Hash>] Array of directory report hashes
|
87
|
+
def directory_reports
|
88
|
+
# Group by directory (absolute path)
|
89
|
+
by_directory = file_reports.group_by { |r| r["directory"] }
|
90
|
+
|
91
|
+
# Aggregate stats for each directory
|
92
|
+
by_directory.map do |directory, reports|
|
93
|
+
# Convert directory to relative path
|
94
|
+
relative_directory = begin
|
95
|
+
Pathname.new(directory).relative_path_from(Pathname.pwd).to_s
|
96
|
+
rescue ArgumentError
|
97
|
+
# If paths are on different drives or otherwise incompatible,
|
98
|
+
# fall back to the absolute path
|
99
|
+
directory
|
100
|
+
end
|
101
|
+
|
102
|
+
total = reports.sum { |r| r["total"] }
|
103
|
+
documented = reports.sum { |r| r["documented"] }
|
104
|
+
coverage = total.positive? ? (documented.to_f / total) * 100 : 100.0
|
105
|
+
|
106
|
+
{
|
107
|
+
"directory" => relative_directory,
|
108
|
+
"total" => total,
|
109
|
+
"documented" => documented,
|
110
|
+
"undocumented" => total - documented,
|
111
|
+
"coverage" => coverage,
|
112
|
+
"files" => reports.size,
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Convert report to a hash representation
|
118
|
+
# @return [Hash] The report as a hash
|
119
|
+
def to_h
|
120
|
+
{
|
121
|
+
"overall" => {
|
122
|
+
"total" => @total_entities.size,
|
123
|
+
"documented" => @documented_entities.size,
|
124
|
+
"undocumented" => @undocumented_entities.size,
|
125
|
+
"coverage" => coverage_percentage,
|
126
|
+
},
|
127
|
+
"directories" => directory_reports,
|
128
|
+
"files" => file_reports,
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
# Process the repository and collect coverage information
|
135
|
+
def process_repository
|
136
|
+
return unless @repository
|
137
|
+
|
138
|
+
@repository.schemas.each do |schema|
|
139
|
+
schema_report = process_schema(schema)
|
140
|
+
@schema_reports << schema_report
|
141
|
+
|
142
|
+
@total_entities.concat(schema_report[:total])
|
143
|
+
@documented_entities.concat(schema_report[:documented])
|
144
|
+
@undocumented_entities.concat(schema_report[:undocumented].map do |entity|
|
145
|
+
{ schema: schema.id, entity: entity }
|
146
|
+
end)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Process a schema and collect coverage information
|
151
|
+
# @param schema [Expressir::Model::Declarations::Schema] The schema to analyze
|
152
|
+
# @return [Hash] A hash with coverage information
|
153
|
+
def process_schema(schema)
|
154
|
+
entities = Expressir::Coverage.find_entities(schema, @skip_types)
|
155
|
+
documented = entities.select { |e| Expressir::Coverage.entity_documented?(e) }
|
156
|
+
undocumented = entities - documented
|
157
|
+
|
158
|
+
coverage = entities.empty? ? 100.0 : (documented.size.to_f / entities.size) * 100
|
159
|
+
|
160
|
+
{
|
161
|
+
schema: schema,
|
162
|
+
total: entities,
|
163
|
+
documented: documented,
|
164
|
+
undocumented: undocumented.map { |e| format_entity(e) },
|
165
|
+
coverage: coverage,
|
166
|
+
}
|
167
|
+
end
|
168
|
+
|
169
|
+
# Format an entity for display
|
170
|
+
# @param entity [Expressir::Model::ModelElement] The entity to format
|
171
|
+
# @return [Hash] A hash with type and name of the entity
|
172
|
+
def format_entity(entity)
|
173
|
+
# Get class name (e.g., "Type" from "Expressir::Model::Declarations::Type")
|
174
|
+
type_name = entity.class.name.split("::").last
|
175
|
+
|
176
|
+
# Convert to EXPRESS convention (e.g., "TYPE")
|
177
|
+
express_type = type_name.upcase
|
178
|
+
|
179
|
+
# Return structured format
|
180
|
+
{
|
181
|
+
"type" => express_type,
|
182
|
+
"name" => entity.id.to_s,
|
183
|
+
}
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Check if an entity has documentation (remarks)
|
188
|
+
# @param entity [Expressir::Model::ModelElement] The entity to check
|
189
|
+
# @return [Boolean] True if the entity has documentation
|
190
|
+
def self.entity_documented?(entity)
|
191
|
+
# Check for direct remarks
|
192
|
+
if entity.respond_to?(:remarks) && entity.remarks && !entity.remarks.empty?
|
193
|
+
return true
|
194
|
+
end
|
195
|
+
|
196
|
+
# Check for remark_items
|
197
|
+
if entity.respond_to?(:remark_items) && entity.remark_items && !entity.remark_items.empty?
|
198
|
+
return true
|
199
|
+
end
|
200
|
+
|
201
|
+
# For schema entities, check if there's a remark_item with their ID
|
202
|
+
if entity.parent.respond_to?(:remark_items) && entity.parent.remark_items
|
203
|
+
entity_id = entity.id.to_s.downcase
|
204
|
+
entity.parent.remark_items.any? do |item|
|
205
|
+
item.id.to_s.downcase == entity_id || item.id.to_s.downcase.include?("#{entity_id}.")
|
206
|
+
end
|
207
|
+
else
|
208
|
+
false
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# Find all entities in a schema
|
213
|
+
# @param schema [Expressir::Model::Declarations::Schema] The schema to analyze
|
214
|
+
# @param skip_types [Array<String>] Array of entity type names to skip from coverage
|
215
|
+
# @return [Array<Expressir::Model::ModelElement>] Array of entities
|
216
|
+
def self.find_entities(schema, skip_types = [])
|
217
|
+
entities = []
|
218
|
+
|
219
|
+
# Add all schema-level entities
|
220
|
+
entities.concat(schema.constants) if schema.constants
|
221
|
+
entities.concat(schema.types) if schema.types
|
222
|
+
entities.concat(schema.entities) if schema.entities
|
223
|
+
entities.concat(schema.functions) if schema.functions
|
224
|
+
entities.concat(schema.rules) if schema.rules
|
225
|
+
entities.concat(schema.procedures) if schema.procedures
|
226
|
+
entities.concat(schema.subtype_constraints) if schema.subtype_constraints
|
227
|
+
|
228
|
+
# Add enumeration items from types (only if TYPE is not being skipped)
|
229
|
+
unless skip_types.include?("TYPE")
|
230
|
+
schema.types&.each do |type|
|
231
|
+
if type.respond_to?(:enumeration_items) && type.enumeration_items
|
232
|
+
entities.concat(type.enumeration_items)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Filter out any nil elements and ensure all have IDs
|
238
|
+
entities = entities.compact.select { |e| e.respond_to?(:id) && e.id }
|
239
|
+
|
240
|
+
# Filter out skipped entity types
|
241
|
+
if skip_types.any?
|
242
|
+
skip_classes = skip_types.map { |type| ENTITY_TYPE_MAP[type] }.compact
|
243
|
+
entities = entities.reject { |entity| skip_classes.include?(entity.class.name) }
|
244
|
+
end
|
245
|
+
|
246
|
+
entities
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -219,13 +219,15 @@ module Expressir
|
|
219
219
|
" ",
|
220
220
|
].join
|
221
221
|
end,
|
222
|
-
*if node.supertype_attribute && node.id
|
222
|
+
*if node.supertype_attribute && node.id != node.supertype_attribute.attribute.id
|
223
223
|
[
|
224
224
|
"RENAMED",
|
225
225
|
" ",
|
226
|
+
node.id,
|
227
|
+
" ",
|
226
228
|
].join
|
227
229
|
end,
|
228
|
-
*if node.id
|
230
|
+
*if node.id && !node.supertype_attribute
|
229
231
|
[
|
230
232
|
node.id,
|
231
233
|
" ",
|
@@ -67,7 +67,7 @@ module Expressir
|
|
67
67
|
end
|
68
68
|
rule(:aliasStmt) do
|
69
69
|
(tALIAS >> variableId >> tFOR >> generalRef >> qualifier.repeat.as(:qualifier) >>
|
70
|
-
op_delim >> stmt.repeat(1).as(:stmt) >> tEND_ALIAS >>
|
70
|
+
op_delim >> stmt.repeat(1).as(:stmt) >> tEND_ALIAS >> op_delim.as(:op_delim2)).as(:aliasStmt)
|
71
71
|
end
|
72
72
|
rule(:anyKeyword) { KEYWORDS.map { |kw| send("t#{kw}") }.inject(:|) }
|
73
73
|
rule(:arrayType) do
|
@@ -116,7 +116,7 @@ module Expressir
|
|
116
116
|
rule(:element) { (expression >> (op_colon >> repetition).maybe).as(:element) }
|
117
117
|
rule(:embeddedRemark) { (str("(*") >> (str("*)").absent? >> (embeddedRemark | any)).repeat >> str("*)")).as(:embeddedRemark) }
|
118
118
|
rule(:encodedCharacter) { (octet >> octet >> octet >> octet) }
|
119
|
-
rule(:encodedStringLiteral) { cts((str('"') >>
|
119
|
+
rule(:encodedStringLiteral) { cts((str('"') >> encodedCharacter.repeat(1) >> str('"')).as(:str)).as(:encodedStringLiteral) }
|
120
120
|
rule(:entityBody) do
|
121
121
|
(explicitAttr.repeat.as(:explicitAttr) >> deriveClause.maybe >> inverseClause.maybe >> uniqueClause.maybe >> whereClause.maybe).as(:entityBody)
|
122
122
|
end
|
@@ -149,7 +149,7 @@ module Expressir
|
|
149
149
|
rule(:functionHead) do
|
150
150
|
(tFUNCTION >> functionId >>
|
151
151
|
(op_leftparen >> (formalParameter >> (op_delim >> formalParameter).repeat).as(:listOf_formalParameter) >> op_rightparen).maybe >>
|
152
|
-
op_colon >> parameterType >>
|
152
|
+
op_colon >> parameterType >> op_delim.as(:op_delim2)).as(:functionHead)
|
153
153
|
end
|
154
154
|
rule(:functionId) { simpleId.as(:functionId) }
|
155
155
|
rule(:functionRef) { functionId.as(:functionRef) }
|
@@ -181,7 +181,7 @@ module Expressir
|
|
181
181
|
rule(:intervalItem) { simpleExpression.as(:intervalItem) }
|
182
182
|
rule(:intervalLow) { simpleExpression.as(:intervalLow) }
|
183
183
|
rule(:interval) do
|
184
|
-
(op_left_curly_brace >> intervalLow >> intervalOp >> intervalItem >>
|
184
|
+
(op_left_curly_brace >> intervalLow >> intervalOp >> intervalItem >> intervalOp.as(:intervalOp2) >>
|
185
185
|
intervalHigh >> op_right_curly_brace).as(:interval)
|
186
186
|
end
|
187
187
|
rule(:intervalOp) { (op_less_equal | op_less_than).as(:intervalOp) }
|
@@ -256,7 +256,7 @@ module Expressir
|
|
256
256
|
rule(:procedureHead) do
|
257
257
|
(tPROCEDURE >> procedureId >>
|
258
258
|
(op_leftparen >> (procedureHeadParameter >>
|
259
|
-
(op_delim >> procedureHeadParameter).repeat).as(:listOf_procedureHeadParameter) >> op_rightparen).maybe >>
|
259
|
+
(op_delim >> procedureHeadParameter).repeat).as(:listOf_procedureHeadParameter) >> op_rightparen).maybe >> op_delim.as(:op_delim2)).as(:procedureHead)
|
260
260
|
end
|
261
261
|
rule(:procedureId) { simpleId.as(:procedureId) }
|
262
262
|
rule(:procedureRef) { procedureId.as(:procedureRef) }
|
@@ -281,7 +281,7 @@ module Expressir
|
|
281
281
|
end
|
282
282
|
rule(:renameId) { (constantId | entityId | functionId | procedureId | typeId).as(:renameId) }
|
283
283
|
rule(:repeatControl) { (incrementControl.maybe >> whileControl.maybe >> untilControl.maybe).as(:repeatControl) }
|
284
|
-
rule(:repeatStmt) { (tREPEAT >> repeatControl >> op_delim >> stmt.repeat(1).as(:stmt) >> tEND_REPEAT >>
|
284
|
+
rule(:repeatStmt) { (tREPEAT >> repeatControl >> op_delim >> stmt.repeat(1).as(:stmt) >> tEND_REPEAT >> op_delim.as(:op_delim2)).as(:repeatStmt) }
|
285
285
|
rule(:repetition) { numericExpression.as(:repetition) }
|
286
286
|
rule(:resourceOrRename) { (resourceRef >> (tAS >> renameId).maybe).as(:resourceOrRename) }
|
287
287
|
rule(:resourceRef) { (constantRef | entityRef | functionRef | procedureRef | typeRef).as(:resourceRef) }
|
@@ -301,7 +301,7 @@ module Expressir
|
|
301
301
|
schemaBodyDeclaration.repeat.as(:schemaBodyDeclaration)).as(:schemaBody)
|
302
302
|
end
|
303
303
|
rule(:schemaDecl) do
|
304
|
-
(tSCHEMA >> schemaId >> schemaVersionId.maybe >> op_delim >> schemaBody >> tEND_SCHEMA >>
|
304
|
+
(tSCHEMA >> schemaId >> schemaVersionId.maybe >> op_delim >> schemaBody >> tEND_SCHEMA >> op_delim.as(:op_delim2)).as(:schemaDecl)
|
305
305
|
end
|
306
306
|
rule(:schemaId) { simpleId.as(:schemaId) }
|
307
307
|
rule(:schemaRef) { schemaId.as(:schemaRef) }
|
@@ -353,7 +353,7 @@ module Expressir
|
|
353
353
|
(tTOTAL_OVER >> op_leftparen >> (entityRef >> (op_comma >> entityRef).repeat).as(:listOf_entityRef) >> op_rightparen >> op_delim).as(:totalOver)
|
354
354
|
end
|
355
355
|
rule(:typeDecl) do
|
356
|
-
(tTYPE >> typeId >> op_equals >> underlyingType >> op_delim >> whereClause.maybe >> tEND_TYPE >>
|
356
|
+
(tTYPE >> typeId >> op_equals >> underlyingType >> op_delim >> whereClause.maybe >> tEND_TYPE >> op_delim.as(:op_delim2)).as(:typeDecl)
|
357
357
|
end
|
358
358
|
rule(:typeId) { simpleId.as(:typeId) }
|
359
359
|
rule(:typeLabelId) { simpleId.as(:typeLabelId) }
|
@@ -363,7 +363,7 @@ module Expressir
|
|
363
363
|
rule(:unaryOp) { (op_plus | op_minus | tNOT).as(:unaryOp) }
|
364
364
|
rule(:underlyingType) { (concreteTypes | constructedTypes).as(:underlyingType) }
|
365
365
|
rule(:uniqueClause) do
|
366
|
-
(tUNIQUE >> (uniqueRule >> op_delim >> (uniqueRule >>
|
366
|
+
(tUNIQUE >> (uniqueRule >> op_delim >> (uniqueRule >> op_delim.as(:op_delim2)).repeat).as(:listOf_uniqueRule)).as(:uniqueClause)
|
367
367
|
end
|
368
368
|
rule(:uniqueRule) do
|
369
369
|
((ruleLabelId >> op_colon).maybe >> (referencedAttribute >> (op_comma >> referencedAttribute).repeat).as(:listOf_referencedAttribute)).as(:uniqueRule)
|
@@ -88,7 +88,7 @@ module Expressir
|
|
88
88
|
private_methods.grep(/^visit_/).map do |name|
|
89
89
|
rulename = name.to_s.sub(/^visit_/, "").gsub(/_([a-z])/) { $1.upcase }
|
90
90
|
[rulename.to_sym, name.to_sym]
|
91
|
-
end
|
91
|
+
end,
|
92
92
|
]
|
93
93
|
end
|
94
94
|
|
@@ -104,7 +104,7 @@ module Expressir
|
|
104
104
|
else
|
105
105
|
[k, to_ctx(v, k)]
|
106
106
|
end
|
107
|
-
end
|
107
|
+
end,
|
108
108
|
]
|
109
109
|
Ctx.new nodes, name
|
110
110
|
when Array
|
@@ -581,7 +581,13 @@ module Expressir
|
|
581
581
|
ctx__redeclared_attribute__qualified_attribute = ctx__redeclared_attribute&.qualified_attribute
|
582
582
|
ctx__redeclared_attribute__attribute_id = ctx__redeclared_attribute&.attribute_id
|
583
583
|
|
584
|
-
|
584
|
+
attribute_qualifier = ctx__redeclared_attribute__qualified_attribute&.attribute_qualifier
|
585
|
+
attribute_id = attribute_qualifier&.attribute_ref&.attribute_id
|
586
|
+
ctx__redeclared_attribute__qualified_attribute__attribute_qualifier_attribute_id = attribute_id
|
587
|
+
|
588
|
+
id = visit_if(ctx__attribute_id ||
|
589
|
+
ctx__redeclared_attribute__attribute_id ||
|
590
|
+
ctx__redeclared_attribute__qualified_attribute__attribute_qualifier_attribute_id)
|
585
591
|
supertype_attribute = visit_if(ctx__redeclared_attribute__qualified_attribute)
|
586
592
|
|
587
593
|
Model::Declarations::Attribute.new(
|
@@ -806,9 +812,8 @@ module Expressir
|
|
806
812
|
type = visit_if(ctx__parameter_type)
|
807
813
|
expression = visit_if(ctx__expression)
|
808
814
|
|
809
|
-
Model::Declarations::
|
810
|
-
id: attribute.id,
|
811
|
-
kind: Model::Declarations::Attribute::DERIVED,
|
815
|
+
Model::Declarations::DerivedAttribute.new(
|
816
|
+
id: attribute.id,
|
812
817
|
supertype_attribute: attribute.supertype_attribute, # reuse
|
813
818
|
type: type,
|
814
819
|
expression: expression,
|
@@ -1428,9 +1433,8 @@ module Expressir
|
|
1428
1433
|
visit(ctx__attribute_ref)
|
1429
1434
|
end
|
1430
1435
|
|
1431
|
-
Model::Declarations::
|
1436
|
+
Model::Declarations::InverseAttribute.new(
|
1432
1437
|
id: attribute.id, # reuse
|
1433
|
-
kind: Model::Declarations::Attribute::INVERSE,
|
1434
1438
|
supertype_attribute: attribute.supertype_attribute, # reuse
|
1435
1439
|
type: type,
|
1436
1440
|
expression: expression,
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Expressir
|
2
|
+
module Model
|
3
|
+
module Declarations
|
4
|
+
# Specified in ISO 10303-11:2004
|
5
|
+
# - section 9.2.1 Attributes
|
6
|
+
class DerivedAttribute < Attribute
|
7
|
+
include Identifier
|
8
|
+
|
9
|
+
DERIVED = "DERIVED".freeze
|
10
|
+
|
11
|
+
attribute :kind, :string, default: -> { DERIVED }
|
12
|
+
attribute :_class, :string, default: -> { send(:name) }
|
13
|
+
|
14
|
+
key_value do
|
15
|
+
map "_class", to: :_class, render_default: true
|
16
|
+
map "kind", to: :kind, render_default: true
|
17
|
+
map "supertype_attribute", to: :supertype_attribute
|
18
|
+
map "optional", to: :optional
|
19
|
+
map "type", to: :type
|
20
|
+
map "expression", to: :expression
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Expressir
|
2
|
+
module Model
|
3
|
+
module Declarations
|
4
|
+
# Specified in ISO 10303-11:2004
|
5
|
+
# - section 9.2.1 Attributes
|
6
|
+
class InverseAttribute < Attribute
|
7
|
+
include Identifier
|
8
|
+
|
9
|
+
INVERSE = "INVERSE".freeze
|
10
|
+
|
11
|
+
attribute :kind, :string, default: -> { INVERSE }
|
12
|
+
attribute :_class, :string, default: -> { send(:name) }
|
13
|
+
|
14
|
+
key_value do
|
15
|
+
map "_class", to: :_class, render_default: true
|
16
|
+
map "kind", to: :kind, render_default: true
|
17
|
+
map "supertype_attribute", to: :supertype_attribute
|
18
|
+
map "optional", to: :optional
|
19
|
+
map "type", to: :type
|
20
|
+
map "expression", to: :expression
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -56,6 +56,8 @@ module Expressir
|
|
56
56
|
"Expressir::Model::DataTypes::Set",
|
57
57
|
"Expressir::Model::DataTypes::String" =>
|
58
58
|
"Expressir::Model::DataTypes::String",
|
59
|
+
"Expressir::Model::Declarations::DerivedAttribute" =>
|
60
|
+
"Expressir::Model::Declarations::DerivedAttribute",
|
59
61
|
"Expressir::Model::Declarations::Attribute" =>
|
60
62
|
"Expressir::Model::Declarations::Attribute",
|
61
63
|
"Expressir::Model::Declarations::Constant" =>
|
data/lib/expressir/version.rb
CHANGED
data/lib/expressir.rb
CHANGED
@@ -3,6 +3,7 @@ require_relative "expressir/version"
|
|
3
3
|
require_relative "expressir/cli"
|
4
4
|
require_relative "expressir/config"
|
5
5
|
require_relative "expressir/benchmark"
|
6
|
+
require_relative "expressir/coverage"
|
6
7
|
require "lutaml/model"
|
7
8
|
require "liquid" # To enable Lutaml::Model::Liquefiable
|
8
9
|
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: expressir
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.19
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-06-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: base64
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: benchmark-ips
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +94,34 @@ dependencies:
|
|
80
94
|
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '2.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: ruby-progressbar
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.11'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.11'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: terminal-table
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3.0'
|
83
125
|
- !ruby/object:Gem::Dependency
|
84
126
|
name: thor
|
85
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,10 +184,18 @@ files:
|
|
142
184
|
- lib/expressir.rb
|
143
185
|
- lib/expressir/benchmark.rb
|
144
186
|
- lib/expressir/cli.rb
|
187
|
+
- lib/expressir/commands/base.rb
|
188
|
+
- lib/expressir/commands/benchmark.rb
|
189
|
+
- lib/expressir/commands/benchmark_cache.rb
|
190
|
+
- lib/expressir/commands/clean.rb
|
191
|
+
- lib/expressir/commands/coverage.rb
|
192
|
+
- lib/expressir/commands/format.rb
|
193
|
+
- lib/expressir/commands/validate.rb
|
194
|
+
- lib/expressir/commands/version.rb
|
145
195
|
- lib/expressir/config.rb
|
196
|
+
- lib/expressir/coverage.rb
|
146
197
|
- lib/expressir/express/cache.rb
|
147
198
|
- lib/expressir/express/error.rb
|
148
|
-
- lib/expressir/express/express_remarks_decorator.rb
|
149
199
|
- lib/expressir/express/formatter.rb
|
150
200
|
- lib/expressir/express/hyperlink_formatter.rb
|
151
201
|
- lib/expressir/express/model_visitor.rb
|
@@ -175,11 +225,13 @@ files:
|
|
175
225
|
- lib/expressir/model/data_types/string.rb
|
176
226
|
- lib/expressir/model/declarations/attribute.rb
|
177
227
|
- lib/expressir/model/declarations/constant.rb
|
228
|
+
- lib/expressir/model/declarations/derived_attribute.rb
|
178
229
|
- lib/expressir/model/declarations/entity.rb
|
179
230
|
- lib/expressir/model/declarations/function.rb
|
180
231
|
- lib/expressir/model/declarations/interface.rb
|
181
232
|
- lib/expressir/model/declarations/interface_item.rb
|
182
233
|
- lib/expressir/model/declarations/interfaced_item.rb
|
234
|
+
- lib/expressir/model/declarations/inverse_attribute.rb
|
183
235
|
- lib/expressir/model/declarations/parameter.rb
|
184
236
|
- lib/expressir/model/declarations/procedure.rb
|
185
237
|
- lib/expressir/model/declarations/remark_item.rb
|
@@ -1,54 +0,0 @@
|
|
1
|
-
module Expressir
|
2
|
-
module Express
|
3
|
-
class ExpressRemarksDecorator
|
4
|
-
RELATIVE_PREFIX_MACRO_REGEXP = /^(link|image|video|audio|include)(:+)?(?![^\/:]+:\/\/|[A-Z]:\/|\/)([^:\[]+)(\[.*\])?$/.freeze
|
5
|
-
|
6
|
-
attr_reader :remark, :options
|
7
|
-
|
8
|
-
def self.call(remark, options)
|
9
|
-
new(remark, options).call
|
10
|
-
end
|
11
|
-
|
12
|
-
def initialize(remark, options)
|
13
|
-
@remark = remark
|
14
|
-
@options = options
|
15
|
-
end
|
16
|
-
|
17
|
-
def call
|
18
|
-
result = remark
|
19
|
-
if options["relative_path_prefix"]
|
20
|
-
result = update_relative_paths(result,
|
21
|
-
options["relative_path_prefix"])
|
22
|
-
end
|
23
|
-
result
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def update_relative_paths(string, path_prefix)
|
29
|
-
string
|
30
|
-
.split("\n")
|
31
|
-
.map do |line|
|
32
|
-
if line.match?(RELATIVE_PREFIX_MACRO_REGEXP)
|
33
|
-
prefix_relative_paths(line, path_prefix)
|
34
|
-
else
|
35
|
-
line
|
36
|
-
end
|
37
|
-
end
|
38
|
-
.join("\n")
|
39
|
-
end
|
40
|
-
|
41
|
-
def prefix_relative_paths(line, path_prefix)
|
42
|
-
line.gsub(RELATIVE_PREFIX_MACRO_REGEXP) do |_match|
|
43
|
-
prefixed_path = File.join(path_prefix, $3.strip)
|
44
|
-
# When we are dealing with a relative path of a template:
|
45
|
-
# ../path/to/file we need to transform it into
|
46
|
-
# the absolute one because `image::` macro wont understand it other way
|
47
|
-
prefixed_path = File.absolute_path(prefixed_path) if prefixed_path.start_with?("../")
|
48
|
-
full_path = File.expand_path(prefixed_path)
|
49
|
-
"#{$1}#{$2}#{full_path}#{$4}"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|