cql_qdm_patientapi 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +46 -0
  3. data/.travis.yml +15 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +127 -0
  6. data/LICENSE +201 -0
  7. data/README.md +2 -0
  8. data/README.rdoc +2 -0
  9. data/Rakefile +32 -0
  10. data/app/assets/javascripts/cql_qdm_patientapi/.keep +0 -0
  11. data/app/assets/javascripts/cql_qdm_patientapi.js.coffee +3 -0
  12. data/app/assets/javascripts/cqlpatient.js.coffee +232 -0
  13. data/app/assets/javascripts/datatypes/adverseevent.js.coffee +70 -0
  14. data/app/assets/javascripts/datatypes/allergyintolerance.js.coffee +63 -0
  15. data/app/assets/javascripts/datatypes/assessment.js.coffee +159 -0
  16. data/app/assets/javascripts/datatypes/careexperience.js.coffee +47 -0
  17. data/app/assets/javascripts/datatypes/caregoal.js.coffee +60 -0
  18. data/app/assets/javascripts/datatypes/characteristic_birthdate.js.coffee +28 -0
  19. data/app/assets/javascripts/datatypes/communication.js.coffee +116 -0
  20. data/app/assets/javascripts/datatypes/datatype.js.coffee +44 -0
  21. data/app/assets/javascripts/datatypes/device.js.coffee +163 -0
  22. data/app/assets/javascripts/datatypes/diagnosis.js.coffee +67 -0
  23. data/app/assets/javascripts/datatypes/diagnosticstudy.js.coffee +215 -0
  24. data/app/assets/javascripts/datatypes/encounter.js.coffee +213 -0
  25. data/app/assets/javascripts/datatypes/familyhistory.js.coffee +37 -0
  26. data/app/assets/javascripts/datatypes/immunization.js.coffee +152 -0
  27. data/app/assets/javascripts/datatypes/intervention.js.coffee +164 -0
  28. data/app/assets/javascripts/datatypes/laboratorytest.js.coffee +243 -0
  29. data/app/assets/javascripts/datatypes/medication.js.coffee +486 -0
  30. data/app/assets/javascripts/datatypes/patient_characteristic.js.coffee +23 -0
  31. data/app/assets/javascripts/datatypes/patient_characteristic_expired.js.coffee +38 -0
  32. data/app/assets/javascripts/datatypes/patient_characteristic_payer.js.coffee +33 -0
  33. data/app/assets/javascripts/datatypes/patient_characteristic_sex.js.coffee +23 -0
  34. data/app/assets/javascripts/datatypes/physicalexam.js.coffee +223 -0
  35. data/app/assets/javascripts/datatypes/procedure.js.coffee +296 -0
  36. data/app/assets/javascripts/datatypes/substance.js.coffee +294 -0
  37. data/app/assets/javascripts/datatypes/symptom.js.coffee +49 -0
  38. data/app/assets/javascripts/types/component.js.coffee +71 -0
  39. data/app/assets/javascripts/types/facility.js.coffee +41 -0
  40. data/app/assets/javascripts/types/id.js.coffee +23 -0
  41. data/app/assets/javascripts/utils/helpers.js.coffee +101 -0
  42. data/bin/rails +12 -0
  43. data/coffeelint.json +135 -0
  44. data/cql_qdm_patientapi.gemspec +25 -0
  45. data/lib/cql_qdm_patientapi/engine.rb +6 -0
  46. data/lib/cql_qdm_patientapi/version.rb +3 -0
  47. data/lib/cql_qdm_patientapi.rb +4 -0
  48. data/vendor/assets/javascripts/cql4browsers.js +53992 -0
  49. metadata +133 -0
@@ -0,0 +1,294 @@
1
+ ###
2
+ @namespace scoping into the CQL_QDM namespace (all classes and
3
+ their methods will be accessable through the CQL_QDM namespace)
4
+ ###
5
+ @CQL_QDM ||= {}
6
+
7
+
8
+ ###
9
+ Data elements that meet criteria using this datatype should document that the
10
+ substance indicated by the QDM category and its corresponding value set was
11
+ actually given to the patient.
12
+ ###
13
+ class CQL_QDM.SubstanceAdministered extends CQL_QDM.QDMDatatype
14
+ ###
15
+ @param {Object} entry - the HDS data criteria object to convert
16
+ ###
17
+ constructor: (@entry) ->
18
+ super @entry
19
+ @_authorDatetime = CQL_QDM.Helpers.convertDateTime(@entry.start_time)
20
+ @_dosage = @entry.dose
21
+ @_frequency = @entry.frequency
22
+ @_negationRationale = @entry.negationReason
23
+ @_relevantPeriodLow = CQL_QDM.Helpers.convertDateTime(@entry.start_time)
24
+ if @entry.end_time
25
+ @_relevantPeriodHigh = CQL_QDM.Helpers.convertDateTime(@entry.end_time)
26
+ else
27
+ # No end time; high is set to infinity
28
+ @_relevantPeriodHigh = CQL_QDM.Helpers.infinityDateTime()
29
+ @_route = @entry.route
30
+ @_supply = @entry.supply
31
+
32
+ ###
33
+ Author date time is only present when this data type has been negated.
34
+ @returns {Date}
35
+ ###
36
+ authorDatetime: ->
37
+ @_authorDatetime
38
+
39
+ ###
40
+ @returns {Quantity}
41
+ ###
42
+ dosage: ->
43
+ if @_dosage?
44
+ new cql.Quantity({unit: @_dosage['unit'], value: @_dosage['value']})
45
+ else
46
+ null
47
+
48
+ ###
49
+ @returns {Code}
50
+ ###
51
+ frequency: ->
52
+ # TODO: Frequency currently not in HDS model. Can probably add by calculating based off of dose & regimen
53
+ throw new Error('Bonnie does not currently support SubstanceAdministered.frequency')
54
+ if @_frequency?
55
+ new cql.Code(@_frequency.code, @_frequency.code_system)
56
+ else
57
+ null
58
+
59
+ ###
60
+ @returns {Code}
61
+ ###
62
+ negationRationale: ->
63
+ if @_negationRationale?
64
+ new cql.Code(@_negationRationale.code, @_negationRationale.code_system)
65
+ else
66
+ null
67
+
68
+ ###
69
+ @returns {Interval<Date>}
70
+ ###
71
+ relevantPeriod: ->
72
+ low = @_relevantPeriodLow
73
+ high = @_relevantPeriodHigh
74
+ if low?
75
+ new cql.Interval(low, high)
76
+ else
77
+ null
78
+
79
+ ###
80
+ @returns {Code}
81
+ ###
82
+ route: ->
83
+ if @_route?
84
+ new cql.Code(@_route.code, @_route.code_system)
85
+ else
86
+ null
87
+
88
+ ###
89
+ @returns {Quantity}
90
+ ###
91
+ supply: ->
92
+ if @_supply?
93
+ new cql.Quantity({unit: @_supply['unit'], value: @_supply['value']})
94
+ else
95
+ null
96
+
97
+
98
+ ###
99
+ Data elements that meet criteria using this datatype should document a request
100
+ for the substance indicated by the QDM category and its corresponding value set.
101
+ ###
102
+ class CQL_QDM.SubstanceOrder extends CQL_QDM.QDMDatatype
103
+ ###
104
+ @param {Object} entry - the HDS data criteria object to convert
105
+ ###
106
+ constructor: (@entry) ->
107
+ super @entry
108
+ @_authorDatetime = CQL_QDM.Helpers.convertDateTime(@entry.start_time)
109
+ @_dosage = @entry.dose
110
+ @_frequency = @entry.frequency
111
+ @_method = @entry.method
112
+ @_negationRationale = @entry.negationReason
113
+ @_reason = @entry.reason
114
+ @_refills = @entry.refills
115
+ @_route = @entry.route
116
+ @_supply = @entry.supply
117
+
118
+ ###
119
+ @returns {Date}
120
+ ###
121
+ authorDatetime: ->
122
+ @_authorDatetime
123
+
124
+ ###
125
+ @returns {Quantity}
126
+ ###
127
+ dosage: ->
128
+ if @_dosage?
129
+ new cql.Quantity({unit: @_dosage['unit'], value: @_dosage['value']})
130
+ else
131
+ null
132
+
133
+ ###
134
+ @returns {Code}
135
+ ###
136
+ frequency: ->
137
+ # TODO: Frequency currently not in HDS model. Can probably add by calculating based off of dose & regimen
138
+ throw new Error('Bonnie does not currently support SubstanceOrder.frequency')
139
+ if @_frequency?
140
+ new cql.Code(@_frequency.code, @_frequency.code_system)
141
+ else
142
+ null
143
+
144
+ ###
145
+ @returns {Code}
146
+ ###
147
+ method: ->
148
+ if @_method?
149
+ new cql.Code(@_method.code, @_method.code_system)
150
+ else
151
+ null
152
+
153
+ ###
154
+ @returns {Code}
155
+ ###
156
+ negationRationale: ->
157
+ if @_negationRationale?
158
+ new cql.Code(@_negationRationale.code, @_negationRationale.code_system)
159
+ else
160
+ null
161
+
162
+ ###
163
+ @returns {Code}
164
+ ###
165
+ reason: ->
166
+ if @_reason?
167
+ new cql.Code(@_reason.code, @_reason.code_system)
168
+ else
169
+ null
170
+
171
+ ###
172
+ @returns {Integer}
173
+ ###
174
+ refills: ->
175
+ # TODO: Refills should represent 'The number of refills allowed by the prescription.'
176
+ throw new Error('Bonnie does not currently support SubstanceOrder.refills')
177
+
178
+ ###
179
+ @returns {Code}
180
+ ###
181
+ route: ->
182
+ if @_route?
183
+ new cql.Code(@_route.code, @_route.code_system)
184
+ else
185
+ null
186
+
187
+ ###
188
+ @returns {Quantity}
189
+ ###
190
+ supply: ->
191
+ if @_supply?
192
+ new cql.Quantity({unit: @_supply['unit'], value: @_supply['value']})
193
+ else
194
+ null
195
+
196
+
197
+ ###
198
+ Data elements that meet criteria using this datatype should document a
199
+ recommendation for the substance indicated by the QDM category and its
200
+ corresponding value set.
201
+ ###
202
+ class CQL_QDM.SubstanceRecommended extends CQL_QDM.QDMDatatype
203
+ ###
204
+ @param {Object} entry - the HDS data criteria object to convert
205
+ ###
206
+ constructor: (@entry) ->
207
+ super @entry
208
+ @_authorDatetime = CQL_QDM.Helpers.convertDateTime(@entry.start_time)
209
+ @_dosage = @entry.dose
210
+ @_frequency = @entry.frequency
211
+ @_method = @entry.method
212
+ @_negationRationale = @entry.negationReason
213
+ @_reason = @entry.reason
214
+ @_refills = @entry.refills
215
+ @_route = @entry.route
216
+ @_supply = @entry.supply
217
+
218
+ ###
219
+ @returns {Date}
220
+ ###
221
+ authorDatetime: ->
222
+ @_authorDatetime
223
+
224
+ ###
225
+ @returns {Quantity}
226
+ ###
227
+ dosage: ->
228
+ if @_dosage?
229
+ new cql.Quantity({unit: @_dosage['unit'], value: @_dosage['value']})
230
+ else
231
+ null
232
+
233
+ ###
234
+ @returns {Code}
235
+ ###
236
+ frequency: ->
237
+ # TODO: Frequency currently not in HDS model. Can probably add by calculating based off of dose & regimen
238
+ throw new Error('Bonnie does not currently support SubstanceRecommended.frequency')
239
+ if @_frequency?
240
+ new cql.Code(@_frequency.code, @_frequency.code_system)
241
+ else
242
+ null
243
+
244
+ ###
245
+ @returns {Code}
246
+ ###
247
+ method: ->
248
+ if @_method?
249
+ new cql.Code(@_method.code, @_method.code_system)
250
+ else
251
+ null
252
+
253
+ ###
254
+ @returns {Code}
255
+ ###
256
+ negationRationale: ->
257
+ if @_negationRationale?
258
+ new cql.Code(@_negationRationale.code, @_negationRationale.code_system)
259
+ else
260
+ null
261
+
262
+ ###
263
+ @returns {Code}
264
+ ###
265
+ reason: ->
266
+ if @_reason?
267
+ new cql.Code(@_reason.code, @_reason.code_system)
268
+ else
269
+ null
270
+
271
+ ###
272
+ @returns {Integer}
273
+ ###
274
+ refills: ->
275
+ # TODO: Refills should represent 'The number of refills allowed by the prescription.'
276
+ throw new Error('Bonnie does not currently support SubstanceRecommended.refills')
277
+
278
+ ###
279
+ @returns {Code}
280
+ ###
281
+ route: ->
282
+ if @_route?
283
+ new cql.Code(@_route.code, @_route.code_system)
284
+ else
285
+ null
286
+
287
+ ###
288
+ @returns {Quantity}
289
+ ###
290
+ supply: ->
291
+ if @_supply?
292
+ new cql.Quantity({unit: @_supply['unit'], value: @_supply['value']})
293
+ else
294
+ null
@@ -0,0 +1,49 @@
1
+ ###
2
+ @namespace scoping into the CQL_QDM namespace (all classes and
3
+ their methods will be accessable through the CQL_QDM namespace)
4
+ ###
5
+ @CQL_QDM ||= {}
6
+
7
+
8
+ ###
9
+ Data elements that meet criteria using this datatype should document the
10
+ symptom and its corresponding value set. The onset datetime corresponds to the
11
+ implicit start datetime of the datatype and the abatement datetime corresponds
12
+ to the implicit stop datetime of the datatype. If the abatement datetime is
13
+ null, then the symptom is considered to still be active. When this datatype is
14
+ used with timing relationships, the criterion is looking for whether the
15
+ symptom was active for the time frame indicated by the timing relationships.
16
+ ###
17
+ class CQL_QDM.Symptom extends CQL_QDM.QDMDatatype
18
+ ###
19
+ @param {Object} entry - the HDS data criteria object to convert
20
+ ###
21
+ constructor: (@entry) ->
22
+ super @entry
23
+ @_prevalencePeriodLow = CQL_QDM.Helpers.convertDateTime(@entry.start_time)
24
+ if @entry.end_time
25
+ @_prevalencePeriodHigh = CQL_QDM.Helpers.convertDateTime(@entry.end_time)
26
+ else
27
+ # No end time; high is set to infinity
28
+ @_prevalencePeriodHigh = CQL_QDM.Helpers.infinityDateTime()
29
+ @_severity = @entry.severity
30
+
31
+ ###
32
+ @returns {Interval<Date>}
33
+ ###
34
+ prevalencePeriod: ->
35
+ low = @_prevalencePeriodLow
36
+ high = @_prevalencePeriodHigh
37
+ if low?
38
+ new cql.Interval(low, high)
39
+ else
40
+ null
41
+
42
+ ###
43
+ @returns {Code}
44
+ ###
45
+ severity: ->
46
+ if @_severity?
47
+ new cql.Code(@_severity.code, @_severity.code_system)
48
+ else
49
+ null
@@ -0,0 +1,71 @@
1
+ ###
2
+ @namespace scoping into the CQL_QDM namespace (all classes and
3
+ their methods will be accessable through the CQL_QDM namespace)
4
+ ###
5
+ @CQL_QDM ||= {}
6
+
7
+ ###
8
+ Represents a CQL 'component' for use in the CQL execution engine.
9
+ ###
10
+ class CQL_QDM.Component
11
+   constructor: (component) ->
12
+ @_result = CQL_QDM.Helpers.formatResult(component.result)
13
+ @_code = new cql.Code(component.code.code, component.code.code_system)
14
+
15
+   exec: (ctx) ->
16
+     @
17
+   
18
+   toString: () ->
19
+     "#{@_result.toString()} '#{@_code.toString()}'"
20
+
21
+ ###
22
+ @returns {Code}
23
+ ###
24
+ code: ->
25
+ if @_code?
26
+ @_code
27
+ else
28
+ null
29
+
30
+ ###
31
+ The model_info_file also lists Integer, Decimal, DateTime, Time, and Ratio.
32
+ Decimal and Integer are covered under Quantity with a nil unit.
33
+ Ratio is not yet supported with CQL although it appears in the QDM model.
34
+ Time and Datetime are covered by Date
35
+ @returns {Code|Quantity}
36
+ ###
37
+ result: ->
38
+ if @_result?
39
+ @_result
40
+ else
41
+ null
42
+
43
+ ###
44
+ Represents a CQL 'component' for Laboratory Test Performed, which has a reference range.
45
+ ###
46
+ class CQL_QDM.ResultComponent extends CQL_QDM.Component
47
+ constructor: (component) ->
48
+ super(component)
49
+ referenceRangeLow = component.referenceRangeLow if component.referenceRangeLow.scalar # {"scalar": scalar, "unit": unit}}
50
+ referenceRangeHigh = component.referenceRangeHigh if component.referenceRangeHigh.scalar
51
+ referenceRangeLow_value = referenceRangeLow.scalar if referenceRangeLow
52
+ referenceRangeHigh_value = referenceRangeHigh.scalar if referenceRangeHigh
53
+
54
+ # If only one of low/high is defined, make interval with undefined endpoint
55
+ @_referenceRange = new cql.Interval(referenceRangeLow_value, referenceRangeHigh_value) if referenceRangeLow_value || referenceRangeHigh_value
56
+ # TODO: might need to deal with units in the future
57
+
58
+   exec: (ctx) ->
59
+     @
60
+
61
+ toString: () ->
62
+ "'#{@_referenceRange.toString()}'"
63
+
64
+ ###
65
+ @returns {Interval<Quantity>}
66
+ ###
67
+ referenceRange: ->
68
+ if @_referenceRange?
69
+ @_referenceRange
70
+ else
71
+ null
@@ -0,0 +1,41 @@
1
+ ###
2
+ @namespace scoping into the CQL_QDM namespace (all classes and
3
+ their methods will be accessable through the CQL_QDM namespace)
4
+ ###
5
+ @CQL_QDM ||= {}
6
+
7
+ ###
8
+ Represents a CQL 'facility' for use in the CQL execution engine.
9
+ ###
10
+ class CQL_QDM.FacilityLocation
11
+   constructor: (facility) ->
12
+ @_code = new cql.Code(facility.code.code, facility.code.code_system, undefined, facility.display)
13
+
14
+ locationPeriodLow = CQL_QDM.Helpers.convertDateTime(facility.locationPeriodLow) if facility.locationPeriodLow
15
+ locationPeriodHigh = CQL_QDM.Helpers.convertDateTime(facility.locationPeriodHigh) if facility.locationPeriodHigh
16
+ # If only one of Low/High is defined, make an interval with an undefined start or end
17
+ @_locationPeriod = new cql.Interval(locationPeriodLow, locationPeriodHigh) if locationPeriodLow || locationPeriodHigh
18
+
19
+   exec: (ctx) ->
20
+     @
21
+   
22
+   toString: () ->
23
+     "#{@_locationPeriod} '#{@_code.toString()}'"
24
+
25
+ ###
26
+ @returns {Code}
27
+ ###
28
+ code: ->
29
+ if @_code?
30
+ @_code
31
+ else
32
+ null
33
+
34
+ ###
35
+ @returns {Interval<Date>}
36
+ ###
37
+ locationPeriod: ->
38
+ if @_locationPeriod?
39
+ @_locationPeriod
40
+ else
41
+ null
@@ -0,0 +1,23 @@
1
+ ###
2
+ @namespace scoping into the CQL_QDM namespace (all classes and
3
+ their methods will be accessable through the CQL_QDM namespace)
4
+ ###
5
+ @CQL_QDM ||= {}
6
+
7
+ ###
8
+ Represents the QDM.Id used in the execution engine.
9
+ ###
10
+ class CQL_QDM.Id
11
+ constructor: (@value, @namingSystem) ->
12
+
13
+ ###
14
+ @returns {String}
15
+ ###
16
+ namingSystem: ->
17
+ @namingSystem
18
+
19
+ ###
20
+ @returns {String}
21
+ ###
22
+ value: ->
23
+ @namingSystem
@@ -0,0 +1,101 @@
1
+ ###
2
+ @namespace scoping into the CQL_QDM namespace (all classes and
3
+ their methods will be accessable through the CQL_QDM namespace)
4
+ ###
5
+ @CQL_QDM ||= {}
6
+
7
+
8
+ ###
9
+ Various helper methods.
10
+ ###
11
+ class CQL_QDM.Helpers
12
+
13
+ ###
14
+ Used to convert a Bonnie date + time into a compatible cql DateTime.
15
+
16
+ @param {String} input - the date time to convert
17
+ @returns cql.DateTime
18
+ ###
19
+ @convertDateTime: (input) ->
20
+ if input?
21
+ if moment.utc(input, 'MM/DD/YYYY hh:mm A', true).isValid() || moment.utc(input, 'MM/DD/YYYY h:mm A', true).isValid()
22
+ cql.DateTime.fromDate(moment.utc(input, 'MM/DD/YYYY hh:mm A').toDate(), 0)
23
+ else
24
+ cql.DateTime.fromDate(moment.utc(input, 'X').toDate(), 0)
25
+ else
26
+ null
27
+
28
+ ###
29
+ Returns an 'end of range' cql.DateTime.
30
+ See Section 3.4.1 "System-Defined Types" in the CQL specification.
31
+
32
+ @returns cql.DateTime
33
+ ###
34
+ @infinityDateTime: ->
35
+ cql.DateTime.parse('9999-12-31T23:59:59.999+0000')
36
+
37
+ ###
38
+ For DateTime values makes sure value meets the CQL standard.
39
+ For scalar values:
40
+ - First checks that the value component is numeric
41
+ - Second for the unit component attempts to clean up freetext
42
+ to match a standard version.
43
+
44
+ @param {Result} input - the result object to be parsed into a Quantity
45
+ @returns cql.Quantity
46
+ ###
47
+ @formatResult: (input) ->
48
+ if input?
49
+ if input?.units == 'UnixTime'
50
+ CQL_QDM.Helpers.convertDateTime(input.scalar)
51
+ else if input.codes?
52
+ code_system = Object.keys(input.codes)?[0]
53
+ code = input.codes[code_system]?[0]
54
+ new cql.Code(code, code_system)
55
+ else if input.code?
56
+ code_system = input.code.code_system
57
+ code = input.code.code
58
+ new cql.Code(code, code_system)
59
+ # Check that the scalar portion is a number and the units are a non-zero length string.
60
+ else if (input.scalar?.match(/^[-+]?[0-9]*\.?[0-9]+$/) != null)
61
+ if input.units.length > 0
62
+ new cql.Quantity({unit: input.units , value: parseFloat(input.scalar)})
63
+ else
64
+ parseFloat(input.scalar)
65
+ else
66
+ null
67
+
68
+ ###
69
+ @returns {Array}
70
+ ###
71
+ @relatedTo: (relatedToInput) ->
72
+ relatedToArray = []
73
+ if relatedToInput?
74
+ for relatedTo in relatedToInput
75
+ if relatedTo?
76
+ relatedToArray.push new CQL_QDM.Id(relatedTo.referenced_id)
77
+ relatedToArray
78
+
79
+ ###
80
+ @returns {Array}
81
+ ###
82
+ @components: (componentsInput) ->
83
+ components = []
84
+ if componentsInput?
85
+ for value in componentsInput.values
86
+ if value?
87
+ components.push new CQL_QDM.Component(value)
88
+ components
89
+
90
+ ###
91
+ @returns {Array}
92
+ ###
93
+ @diagnoses: (diagnosesInput, principalDiagnosisInput) ->
94
+ diagnoses = []
95
+ if diagnosesInput?
96
+ for diagnosis in diagnosesInput.values
97
+ if diagnosis?
98
+ diagnoses.push new cql.Code(diagnosis.code, diagnosis.code_system)
99
+ if principalDiagnosisInput?
100
+ diagnoses.push new cql.Code(principalDiagnosisInput.code, principalDiagnosisInput.code_system)
101
+ diagnoses
data/bin/rails ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
3
+
4
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
5
+ ENGINE_PATH = File.expand_path('../../lib/cql_qdm_patientapi/engine', __FILE__)
6
+
7
+ # Set up gems listed in the Gemfile.
8
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
9
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
10
+
11
+ require 'rails/all'
12
+ require 'rails/engine/commands'