ion_orders_engine_mockingjay 1.0.1.SNAPSHOT → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2383f21b8f36036b4fa090ac0e169bc477ebbde5
4
- data.tar.gz: 69615d474475d3da7ae02f0c002d06219e1f8305
3
+ metadata.gz: 09186104869a21657277807bfcccb3f0d85b4ac1
4
+ data.tar.gz: b11ee2ade83e151b3159d832806f0ab0e0cd47de
5
5
  SHA512:
6
- metadata.gz: 43622fb16862f27ba16489d80f4d62afdd102d2275a77d5cd3642c392dda96e8ab48bae5e9f256dc00f7c9b37eee5110d1df5adbc69fa218231afbff61e3845d
7
- data.tar.gz: 0cfe073c305f02403ae085babef6350788e3838b052fc3c3ce0c13151c2140074e63602a92b34524955d75622424249ad51b4c1fe1b800653b8bb1106db39b6d
6
+ metadata.gz: cc2e73a77bf98a169ff2a3e74f8ebc324e44b8f2cd1b692698f430d0a20e5cedc80310945a427b0a0bf488fc618b01b806758420ec94eb2dc3de4e0d16fea261
7
+ data.tar.gz: 202a3931a931c60cf12b27c54c038e04270e6ba36fa950b2a46b20a8a55ee7710933ec5de26deff931f3094a9b73f33d3acf3f874d8b232b60a7ee5dfe8eb542
@@ -0,0 +1,332 @@
1
+ # Public: We provide behavior that allows for the creation of new instances
2
+ # of a model from either a JSON String or a Ruby Hash.
3
+ #
4
+ # Include this module on every model built in this app.
5
+ module Mock::ImportConcern
6
+ # Extensions
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ end
11
+
12
+ # ---------- Instance Methods ----------
13
+
14
+ # No instance methods present in this mixin.
15
+
16
+
17
+ # Public: Checks to see if we have a has_one relation of the specified name.
18
+ # The perspective is that we are the owning resource and we are advising
19
+ # if we have a has_one association with the given name.
20
+ #
21
+ # relation_name - A String or Symbol, such as 'policy' or :policy
22
+ #
23
+ # Returns Boolean true if the model we're mixed into has a 'has_one' relation with the given name.
24
+ def has_a_has_one_relation?(relation_name)
25
+ has_one_keys = self.class.has_one_relations_hash.keys
26
+ sanitized_relation_name = relation_name.to_s
27
+ has_one_keys.include? sanitized_relation_name
28
+ end
29
+
30
+
31
+ # Public: Determines if we are the destination of a has_one relation from our parent
32
+ # resource, assuming we even have a parent resource.
33
+ def is_singular_child_resource?
34
+ parent_model = self.parent_resource_model_object
35
+ if parent_model
36
+ parent_model.has_a_has_one_relation? self.class.to_s.tableize.singularize
37
+ end
38
+ end
39
+
40
+
41
+
42
+ # ---------- Class Methods ----------
43
+
44
+ module ClassMethods
45
+
46
+ # Public: Provides an array of those attributes whose values together, determine non-synthetic uniqueness.
47
+ # We define an empty array here. Classes that use this module mixin should override with there own list of
48
+ # keys as symbols. Never include the :id attribute, because that is synthetic.
49
+ #
50
+ # Returns Array of symbols representing attributes that together, are a business compound primary key.
51
+ def uniqueness_attributes
52
+ []
53
+ end
54
+
55
+
56
+ # Public: Takes a hash of attributes and pulls out just those that match our uniqueness attributes.
57
+ # The resultant hash can be used as conditions in a <emphasis>where</emphasis> clause to query with.
58
+ #
59
+ # contents_hash - The hash of attributes for a candidate instance of this model.
60
+ #
61
+ # Returns Hash containing properties as key-value pairs, which uniquely identify an instance of the mixed
62
+ # in model.
63
+ def uniqueness_conditions(contents_hash)
64
+ conditions_hash = {}
65
+ log_info "Mock::ImportConcern: uniqueness_conditions(): uniqueness_attributes to work with is: #{uniqueness_attributes}"
66
+ uniqueness_attributes.each do | unique_attribute_key |
67
+ #log "Mock::ImportConcern: uniqueness_conditions(): Searching for key: #{unique_attribute_key}"
68
+ # The contents hash we'll be looking through is JSON, with string keys instead of symbol keys,
69
+ # and from testing, apparently that matters! Hence, we do a 'to_s' on each key before looking
70
+ # through the contents_hash for it:
71
+ search_value = contents_hash[unique_attribute_key.to_s]
72
+ if search_value
73
+ #log "Mock::ImportConcern: uniqueness_conditions(): Found value #{search_value} for key: #{unique_attribute_key}"
74
+ conditions_hash[unique_attribute_key] = search_value
75
+ end
76
+ end
77
+
78
+ conditions_hash
79
+ end
80
+
81
+
82
+ # Public: Creates a hash that maps relationship name to model class constant, for our has_one relations.
83
+ #
84
+ # Returns Hash of the mixing in model's ActiveRecord has_one relations with relation names as keys
85
+ # and destination classes as values.
86
+ def has_one_relations_hash
87
+ relations_reflection = self.reflect_on_all_associations(:has_one)
88
+ relations_hash = {}
89
+ relations_reflection.each do |relation|
90
+ relations_hash[relation.name.to_s] = relation.class_name.constantize
91
+ end
92
+
93
+ #log "has_one_relations_hash: #{relations_hash}"
94
+ relations_hash
95
+ end
96
+
97
+
98
+ # Public: Creates a hash that maps relationship name to model class constant, for our has_many relations.
99
+ #
100
+ # Returns Hash of the mixing in model's ActiveRecord has_many relations with relation names as keys
101
+ # and destination classes as values.
102
+ def has_many_relations_hash
103
+ relations_reflection = self.reflect_on_all_associations(:has_many)
104
+ relations_hash = {}
105
+ relations_reflection.each do |relation|
106
+ relations_hash[relation.name.to_s] = relation.class_name.constantize
107
+ end
108
+
109
+ #log "has_many_relations_hash: #{relations_hash}"
110
+ relations_hash
111
+ end
112
+
113
+
114
+ # Public: Takes a Hash of attributes for this model, and does a cross check if there exists
115
+ # an instance of this model already with the same values as those passed in. In doing our check,
116
+ # we <strong>only</strong> consider those attributes present in the array returned from a call
117
+ # to uniqueness_attributes().
118
+ #
119
+ # contents_hash - The hash of attributes for a candidate instance of this model.
120
+ #
121
+ # Returns Boolean whether or not a model instance exists with the provided contents hash.
122
+ def has_existing_instance?(contents_hash)
123
+ conditions_hash = uniqueness_conditions(contents_hash)
124
+ #log "#{self.to_s}: Looking for existing instance using contents_hash: #{contents_hash}"
125
+ #log "#{self.to_s}: Looking for existing instance with attributes: #{conditions_hash}"
126
+
127
+ # Do we have an empty set of conditions?
128
+ if conditions_hash.empty?
129
+ # YES: Return a no match result. This shouldn't happen, but if we allow this answer,
130
+ # every record will be matched with the no-constraint conditions, and then possibly removed.
131
+ log_warn "Interchange::ImportConcern has_existing_instance(): Ran into situation where conditions_hash" \
132
+ "computed was empty. This is dangerous; a wildcard match like this could cause us to delete all" \
133
+ "existing records as duplicates. Ensure uniqueness_attributes array on root models are set correctly."
134
+ false
135
+ else
136
+ # NO: We have some conditions that we can filter on, so we'll do a search based on these
137
+ # conditions and let the existence thereof be our verdict.
138
+ self.where(conditions_hash).exists?
139
+ end
140
+ end
141
+
142
+
143
+ # Internal: Retrieves all instances of this model that match a subset of the values in the
144
+ # contents hash; specifically, the uniqueness values.
145
+ #
146
+ # contents_hash - The hash of attributes for a candidate instance of this model.
147
+ #
148
+ # Return Array of all instances of this model that matched a subset of the values in the contents hash, as
149
+ # defined by the uniqueness_attributes
150
+ def retrieve_existing_instances(contents_hash)
151
+ conditions_hash = uniqueness_conditions(contents_hash)
152
+ self.where(conditions_hash)
153
+ end
154
+
155
+
156
+ # Internal: Returns the first matching existing instance of this model found, otherwise nil.
157
+ #
158
+ # contents_hash - The hash of attributes for a candidate instance of this model.
159
+ #
160
+ # Returns ActiveRecord::Base an instance of this model that matched a subset of the values in the contents hash
161
+ def retrieve_any_existing_instance(contents_hash)
162
+ existing = retrieve_existing_instances(contents_hash)
163
+ unless existing.empty?
164
+ existing.first
165
+ end
166
+ end
167
+
168
+
169
+ # Internal: Finds existing instances of this model that match the uniqueness attributes defined for this model,
170
+ # and destroys them. Presumably, this is to make way for a newly imported instance that would then be a dupe.
171
+ #
172
+ # contents_hash - the hash of attributes for a candidate instance of this model.
173
+ #
174
+ # Returns Fixnum the number of instances we found (and presumably destroyed).
175
+ def remove_duplicates(contents_hash)
176
+ existing_instances = retrieve_existing_instances(contents_hash)
177
+ existing_instances.each do | existing_instance |
178
+ log "#{self.to_s}: remove_duplicates(): Destroying instance #{existing_instance.id}"
179
+ existing_instance.destroy
180
+ end
181
+
182
+ existing_instances.count
183
+ end
184
+
185
+
186
+ # Public: Performs an import operation on the mixing in model, using the model contents provided.
187
+ #
188
+ # Note: when we ourselves call import() on related models, we effectively ignore the duplicate_strategy, as
189
+ # those related models will by definition, always be non-root models. We only care about duplicates when it comes
190
+ # to models that define top level collections (i.e. root models).
191
+ #
192
+ # contents - the contents of a new model to be imported. This may be a proper Ruby Hash.
193
+ # duplicate_strategy - a constant from the Enums::DuplicateStrategy module. This indicates whether duplicates
194
+ # should be allowed, skipped or replace that which they duplicate.
195
+ # This is only meaningful when importing a root model.
196
+ # root_model - if not specified, we'll eventually set it by walking up the parent resource chain
197
+ # on the model we do instantiate (default: parent resource).
198
+ #
199
+ # Returns ActiveRecord::Base the model instance we just built.
200
+ def import(contents, duplicate_strategy, root_model=nil)
201
+ #log "#{self.to_s}: Asked to import data."
202
+
203
+ # Convent the contents passed in to a Hash if it is currently a String of (presumably) JSON.
204
+ #if contents.is_a? String
205
+ # log 'We were passed a String of contents'
206
+ # contents_hash = JSON.parse(contents)
207
+ #else
208
+ # log 'We were passed a Hash of contents'
209
+ # contents_hash = contents
210
+ #end
211
+
212
+ contents_hash = contents
213
+ model = self.new
214
+
215
+ unless root_model
216
+ if model.is_root_resource?
217
+ root_model = model
218
+ root_model.import_errors = [] # Initialize the list of errors
219
+ root_model.import_notices = [] # Initialize the list of notices
220
+
221
+ # Determine if we have duplicates, and if so, handle them as directed by duplicate_strategy:
222
+ apply_duplicate_handling_strategy(contents_hash, duplicate_strategy, root_model)
223
+ end
224
+ end
225
+
226
+ # Apply values from the contents hash onto the model:
227
+ apply_content_to_model(contents_hash, model, root_model)
228
+
229
+ # Return the new model just built; callers are responsible for saving, when appropriate.
230
+ model
231
+ end
232
+
233
+
234
+ # Public: Determines whether we should allow, replace or skip duplicates. We'll first search for a duplicate
235
+ # and if one exists, we'll then apply the duplicate strategy provided in determining how to deal with
236
+ # the duplicate. As we go, we'll add entries to the root model's import_notices array if warranted.
237
+ # For imports that should be skipped, we set the skip_import flag on the root model indicating that
238
+ # it should not be saved.
239
+ #
240
+ # contents_hash - the hash of attributes for a candidate instance of this model.
241
+ # duplicate_strategy - a constant from the Enums::DuplicateStrategy module. This indicates whether duplicates
242
+ # should be allowed, skipped or replace that which they duplicate. This is only meaningful
243
+ # when importing a root model.
244
+ # root_model - if not specified, we'll eventually set it by walking up the parent resource chain
245
+ # on the model we do instantiate.
246
+ #
247
+ # Returns nothing.
248
+ def apply_duplicate_handling_strategy(contents_hash, duplicate_strategy, root_model)
249
+ if has_existing_instance?(contents_hash)
250
+ case duplicate_strategy
251
+ when Enums::DuplicateStrategy::ALLOW
252
+ # Do nothing; allow it if there happened to be a duplicate.
253
+ # Log this in import_notices
254
+ root_model.import_notices << "Imported as an allowed duplicate."
255
+ when Enums::DuplicateStrategy::REPLACE
256
+ # Remove existing entries
257
+ num_dupes_removed = remove_duplicates(contents_hash)
258
+ # Log this in import_notices
259
+ root_model.import_notices << "Imported, but had to delete #{num_dupes_removed} duplicate(s) prior."
260
+ when Enums::DuplicateStrategy::SKIP
261
+ # Log this in import_notices
262
+ root_model.import_notices << "Skipped importing this, as it was a duplicate."
263
+ # Break out of this import; nothing more for us to do. We'll set a return flag to indicate this.
264
+ root_model.skip_import = true
265
+ else
266
+ # Do nothing. Assume our caller has already checked for valid duplicate_strategy values.
267
+ end # case
268
+ end
269
+ end
270
+
271
+
272
+ # Public: Applies the contents hash provided onto the model object provided. This includes
273
+ # core properties of the model, as well as related objects. For related objects,
274
+ # we effectively call into the import() method on the related class, building out
275
+ # our object graph with recursion.
276
+ #
277
+ # contents_hash - the hash of attributes for a candidate instance of this model.
278
+ # model - the model instance we are setting values into from the contents hash.
279
+ # root_model - represents the root model for the model we're currently importing. We need this
280
+ # for when we make re-entrant calls into the import() method for related
281
+ # models to our model instance.
282
+ #
283
+ # Returns nothing.
284
+ def apply_content_to_model(contents_hash, model, root_model)
285
+ has_many_relationships_hash = has_many_relations_hash()
286
+ has_one_relationships_hash = has_one_relations_hash()
287
+
288
+ # Any contents in hash not in a pair will generate an exception. Defend against such malformed input
289
+ # content with an exception handling block.
290
+ begin
291
+ contents_hash.each_pair do |key, value|
292
+ #log "Processing key from contents hash: '#{key}'"
293
+
294
+ next if key == 'id' # Defend against these, which really shouldn't be in the source input files anyways.
295
+
296
+ if model.has_attribute? key
297
+ #log "Processing attribute: #{key} | value: #{value}"
298
+ model.send("#{key}=", value) # Superior to instance_variable_set b/c we're dealing with ActiveRecord attributes
299
+
300
+ elsif has_one_relationships_hash.keys.include? key
301
+ #log "The key: #{key} is one of our has_one relations"
302
+ klazz = has_one_relationships_hash[key]
303
+ if klazz.respond_to? :import
304
+ #log "#{klazz.to_s} responds to import."
305
+ related_has_one_entity = klazz.import(value, Enums::DuplicateStrategy::ALLOW, root_model)
306
+ #log "++About to add '#{related_has_one_entity.class.name}' as a has one to '#{self.name}' via the key '#{key}'"
307
+ mutator_key = "#{key}=".to_sym
308
+ model.send(mutator_key, related_has_one_entity)
309
+ #log "..Completed adding '#{related_has_one_entity.class.name}' as a has one to '#{self.name}' via the key '#{key}'"
310
+ end
311
+
312
+ elsif has_many_relationships_hash.keys.include? key
313
+ #log "The key: #{key} is one of our has_many relations"
314
+ klazz = has_many_relationships_hash[key]
315
+ if klazz.respond_to? :import
316
+ #log "#{klazz.to_s} responds to import."
317
+ value.each do |related_object_data|
318
+ model.send(key) << klazz.import(related_object_data, Enums::DuplicateStrategy::ALLOW, root_model)
319
+ end
320
+ else
321
+ log "#{klazz.to_s} does NOT respond to import."
322
+ end
323
+ end
324
+ end
325
+ rescue Exception => exception
326
+ log_error "ERROR: Ran into an exception importing model #{self.to_s}. #{exception.class.to_s}: #{exception}."
327
+ root_model.import_errors << "#{exception.class.to_s}: #{exception.message}"
328
+ end
329
+ end
330
+
331
+ end # module ClassMethods
332
+ end # module Mock::ImportConcern
@@ -0,0 +1,33 @@
1
+ # This module provides a method that reformats our related phone numbers into a hash structure.
2
+ # This can be useful for many things, though our primary use is to facilitate generating
3
+ # JSON responses via JBuilder
4
+ #
5
+ # Include this module into any model that has_many :phone_numbers.
6
+ module Mock::PhoneNumberConcern
7
+
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ end
12
+
13
+ # Organizes all of our associated PhoneNumbers into a hash structure, where the key is the
14
+ # label and the value is an Array of all raw phone number values that have that given label.
15
+ # This facilitates situations where for example, you have two home phone numbers.
16
+ def formatted_phone_numbers_hash
17
+ numbers_hash = {}
18
+ phone_numbers.each do |phone_number_object|
19
+ iterated_label = phone_number_object.label
20
+ # Does the label already exist in our hash?
21
+ if numbers_hash[iterated_label]
22
+ # YES: Just add the number to the existing value array:
23
+ numbers_hash[iterated_label] << phone_number_object.number
24
+ else
25
+ # NO: Create a new entry for this label:
26
+ numbers_hash[iterated_label] = [phone_number_object.number]
27
+ end
28
+ end
29
+
30
+ numbers_hash
31
+ end
32
+
33
+ end # module
@@ -0,0 +1,86 @@
1
+ # Include this module on every model we use. For non-root models, include the class
2
+ # declaration parent_resource with a symbol identifying what method an instance of that model
3
+ # would call to retrieve its resource parent.
4
+ #
5
+ # For example, in the Allergy.rb model file, we'd have the following line:
6
+ #
7
+ # parent_resource :patient
8
+ #
9
+ # This information allows us to build a model chain up to the root model; useful in constructing
10
+ # navigation structures, such as breadcrumbs.
11
+ #
12
+ module Mock::ResourceAncestorsConcern
13
+
14
+ extend ActiveSupport::Concern
15
+
16
+ included do
17
+ end
18
+
19
+
20
+ def parent_resource_model_object
21
+ self.send(self.class.parent_resource_symbol) if self.class.parent_resource_symbol
22
+ end
23
+
24
+
25
+ def resource_ancestors
26
+ parent = parent_resource_model_object
27
+
28
+ if parent
29
+ parent.resource_ancestors + [self]
30
+ else
31
+ [self]
32
+ end
33
+ end
34
+
35
+
36
+ def root_resource
37
+ resource_ancestors.first unless resource_ancestors.empty?
38
+ end
39
+
40
+
41
+ # We are a root resource if our root resource is ourselves.
42
+ def is_root_resource?
43
+ root_resource == self
44
+ end
45
+
46
+
47
+ # Returns nil by default. Models should override if they'd like
48
+ # a short summary to appear in breadcrumbs.
49
+ def short_title
50
+ nil
51
+ end
52
+
53
+
54
+ # Returns a title string that is more than the short_title; it is preceded by the model class name.
55
+ def model_qualified_title
56
+ "#{self.class.to_s}: #{short_title}"
57
+ end
58
+
59
+
60
+ # We are a protected record if the Patient we ultimately belong to is a protected record.
61
+ # Any root model must implement or have a property such that we can ask it 'protected_record?'.
62
+ # So as not to conflict, this mixin module uses the signature 'marked_as_protected?' to traverse
63
+ # up the parent resource chain.
64
+ def marked_as_protected?
65
+ parent = parent_resource_model_object
66
+
67
+ if parent
68
+ parent.marked_as_protected?
69
+ else
70
+ self.protected_record?
71
+ end
72
+ end
73
+
74
+
75
+ module ClassMethods
76
+ def parent_resource(parent_resource_method_symbol)
77
+ @parent_resource_symbol = parent_resource_method_symbol.to_sym
78
+ end
79
+
80
+ def parent_resource_symbol
81
+ @parent_resource_symbol
82
+ end
83
+ end # module ClassMethods
84
+
85
+
86
+ end # module Mock::ResourceAncestorsConcern
@@ -0,0 +1,41 @@
1
+ # This concern is only meant to be mixed into models that are top-level collections.
2
+ # That is, those models who have no parent model resource.
3
+ module Mock::RootModelConcern
4
+
5
+ # Extensions
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ end
10
+
11
+ # Instance Variables
12
+ attr_accessor :export_errors
13
+ attr_accessor :import_errors
14
+ attr_accessor :import_notices
15
+ attr_accessor :skip_import
16
+
17
+
18
+ # ---------- Instance Methods ----------
19
+
20
+ def skip_import?
21
+ skip_import
22
+ end
23
+
24
+
25
+ # Returns the name to be used for data interchange. Normally, one just returns
26
+ # the name of the class.
27
+ def interchange_type_name
28
+ self.class.to_s
29
+ end
30
+
31
+ # ---------- Class Methods ----------
32
+
33
+ module ClassMethods
34
+ # Our implementing classes should always respond in the affirmative to this query.
35
+ def is_root_resource?
36
+ true
37
+ end
38
+ end # Mock::RootModelConcern::ClassMethods
39
+
40
+
41
+ end # Mock::RootModelConcern
@@ -38,7 +38,7 @@ module Mockingjay
38
38
  # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
39
39
  # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
40
40
  config.active_record.default_timezone = :local
41
- config.active_record.time_zone_aware_attributes = false
41
+ config.active_record.time_zone_aware_attributes = false
42
42
 
43
43
  # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
44
44
  # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
@@ -95,6 +95,4 @@ module Mockingjay
95
95
  end
96
96
 
97
97
  end
98
- end
99
-
100
-
98
+ end
@@ -4,34 +4,11 @@ module Reference
4
4
  # to Ruby classes concretely defined in app/models/reference. Only include those root level reference classes;
5
5
  # any nested classes should be bootstrapped from the initialize() method of the parent reference class.
6
6
  ROOT_REFERENCE_MODEL_CLASSES = [
7
- Reference::LabGroup,
8
- Reference::LabResultDataType,
9
- Reference::AllergyReactionType,
10
- Reference::ConditionStatusType,
11
- Reference::NotificationConceptType,
12
- Reference::AllergyStatusType,
13
- Reference::AllergySeverity,
14
- Reference::AllergyCancelReason,
15
- Reference::VisitType,
16
- Reference::VitalsMeasurementType,
17
- Reference::VitalsMeasurementStatusType,
18
- Reference::VitalsMeasurementNormalcy,
19
- Reference::VitalsMeasurementUnit,
20
- Reference::VitalsMeasurementContributorSystem,
21
- Reference::VitalsMeasurementSystem,
22
- Reference::VitalsMeasurementResultType,
23
- Reference::DiagnosisConfirmationStatus,
24
- Reference::ProblemClassification,
25
- Reference::DiagType,
26
- Reference::LifeCycleStatus,
27
- Reference::ClinicalDiagnosisClinicalService,
28
- Reference::HealthRecordCancelReason
29
7
  ]
30
8
 
31
9
  # This Array holds only those classes that are nested reference models, (ultimately) dependent on one class in the
32
10
  # ROOT_REFERENCE_MODEL_CLASSES Array.
33
11
  NESTED_REFERENCE_MODEL_CLASSES = [
34
- Reference::LabConcept
35
12
  ]
36
13
 
37
14
  # Where reference data is read from and written to
data/config/routes.rb CHANGED
@@ -1,4 +1,4 @@
1
- Mockingjay.Application.routes.draw do
1
+ Mockingjay::Application.routes.draw do
2
2
  match '/patients/:patientId/orders/inpatient' => 'api/ion_orders_engine/order_profile/inpatient_order_profile#show', via: :get, :constraints => {:patientId => /\d+/}
3
3
  match '/patients/:patientId/orders/:orderId' => 'api/ion_orders_engine/order_by_id/order_by_id#show', via: :get, :constraints => {:patientId => /\d+/, :orderId => /\d+/}
4
- end
4
+ end # Mockingjay::Application.routes.draw
File without changes
@@ -0,0 +1,34 @@
1
+ # CREDIT: https://gist.github.com/1933884
2
+ #
3
+ # The BootstrapBreadcrumbsBuilder is a Bootstrap compatible breadcrumb builder.
4
+ # It provides basic functionalities to render a breadcrumb navigation according to Bootstrap's conventions.
5
+ #
6
+ # BootstrapBreadcrumbsBuilder accepts a limited set of options:
7
+ # * separator: what should be displayed as a separator between elements
8
+ #
9
+ # You can use it with the :builder option on render_breadcrumbs:
10
+ # <%= render_breadcrumbs :builder => ::BootstrapBreadcrumbsBuilder, :separator => "&raquo;" %>
11
+ #
12
+ # Note: You may need to adjust the autoload_paths in your config/application.rb file for rails to load this class:
13
+ # config.autoload_paths += Dir["#{config.root}/lib/"]
14
+ #
15
+ class BootstrapBreadcrumbsBuilder < BreadcrumbsOnRails::Breadcrumbs::Builder
16
+ def render
17
+ @context.content_tag(:ul, class: 'breadcrumb') do
18
+ @elements.collect do |element|
19
+ render_element(element)
20
+ end.join.html_safe
21
+ end
22
+ end
23
+
24
+ def render_element(element)
25
+ current = @context.current_page?(compute_path(element))
26
+
27
+ @context.content_tag(:li, class: ('active' if current)) do
28
+ link_or_text = @context.link_to_unless_current(compute_name(element), compute_path(element), element.options)
29
+ divider = @context.content_tag(:span, (@options[:separator] || '/').html_safe, class: 'divider') unless current
30
+
31
+ link_or_text + (divider || '')
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,25 @@
1
+ module Enums
2
+
3
+ # Public: Contains all valid options as Fixnum constants for the various ways that potential
4
+ # duplicate records can be handled.
5
+ module DuplicateStrategy
6
+ ALLOW = 0
7
+ REPLACE = 1
8
+ SKIP = 2
9
+
10
+ ALL_OPTIONS = [ALLOW, REPLACE, SKIP]
11
+
12
+ # Public: Will advise if the provided value passed in is valid.
13
+ # Note: This is a module method, not an 'instance' method, as the generated documentation
14
+ # might imply.
15
+ #
16
+ # value - String value to test for validity.
17
+ #
18
+ # Returns Boolean whether the value provided is valid.
19
+ def valid?(value)
20
+ ALL_OPTIONS.include? value
21
+ end
22
+
23
+ end # module DuplicateStrategy
24
+
25
+ end # module Enums
data/lib/enums.rb ADDED
@@ -0,0 +1,4 @@
1
+ # Public: A module for namespacing so that contained classes are more easily identified
2
+ # as having the specific purpose of being a vessel for enumerated values.
3
+ module Enums
4
+ end
@@ -0,0 +1,176 @@
1
+ # This module deals with importing and exporting model data. We are a counterpart
2
+ # to controllers under the same Interchange module.
3
+ module Interchange
4
+
5
+ # Public: Handles saving model data (exporting) to the local file system, be it intended for
6
+ # later imports back into Mockingjay, or for use as test data for UIAutomation.
7
+ class Writer
8
+ # Mixins
9
+ include ::Mixins::Logging
10
+ include ::ActionView::Helpers::TextHelper # for pluralize
11
+
12
+ # A Hash of results from the last operation, containing keys:
13
+ # :successful - Array of model instances whose export was successful.
14
+ # :failures - Array of model instances whose export was unsuccessful.
15
+ attr_accessor :operation_results
16
+
17
+
18
+ # Public: This demarcates a new export operation. You must provide a block
19
+ # for us to yield to when you call this method. That block should
20
+ # contain one or more calls to our export() method.
21
+ #
22
+ # We reset the class variable 'operation_results' and populate it
23
+ # after yielding, with summary information about the export operation.
24
+ #
25
+ # Returns nothing.
26
+ def mark_export_operation
27
+ log "Starting Export operation."
28
+ # Clear the class cached last-export summary:
29
+ self.class.instance_variable_set(:@last_export_summary, nil)
30
+ @operation_results = { successful: [], failures: [] }
31
+
32
+ yield # Hand over control to the block that calls our export() method one or more times
33
+
34
+ log "Completed Export operation."
35
+ summarize_operation
36
+ self.class.instance_variable_set(:@last_export_summary, @operation_results)
37
+ end
38
+
39
+
40
+ # Public: This inspects the @operation_results Hash and adds more entries to it that describe
41
+ # and timestamp the operation. Included in this is a 'flash_hash' that a controller can
42
+ # use after the export operation, to present a one line summary.
43
+ #
44
+ # Returns nothing.
45
+ def summarize_operation
46
+ message = ''
47
+
48
+ # Failures:
49
+ if @operation_results[:failures].empty?
50
+ flash_key = :notice
51
+ @operation_results[:has_failures] = false
52
+ else
53
+ flash_key = :alert
54
+ @operation_results[:has_failures] = true
55
+ message = "#{@operation_results[:failures].count} #{@model_class.to_s} record(s) failed to export. "
56
+ end
57
+
58
+ # Summary Info:
59
+ @operation_results[:total_records] = @operation_results[:failures].count + @operation_results[:successful].count
60
+ message.concat(pluralize(@operation_results[:successful].count, "#{@model_class.to_s} record")).concat(' exported successfully. ')
61
+ @operation_results[:flash_hash] = { flash_key => message }
62
+ @operation_results[:timestamp] = Time.now
63
+ end
64
+
65
+
66
+ # Public: This determines which specific objects will get exported, and then farms that out to the create_file()
67
+ # method to actually write to the file system.
68
+ #
69
+ # We can handle multiple objects or a single object as well as regular exports and those
70
+ # meant for UIAutomation test data. Most of these options are specified in the optional options hash.
71
+ # In all cases, each call to this method is for dealing with objects of a specific model class.
72
+ #
73
+ # model_class - Class of model to be exported.
74
+ # options - Hash of options that help us identify what subset of data (instances belonging to the model class)
75
+ # that we are meant to export. These options can include:
76
+ # :ids - what ID values to limit the export to (default: nil)
77
+ # :keywords - what keywords a model must have at least one of, in order to qualify for export (default: nil)
78
+ def export(model_class, options = {})
79
+ ids = options[:ids]
80
+ keywords = options[:keywords]
81
+ base_path = options[:base_path]
82
+
83
+ log "keywords sought: #{keywords}"
84
+
85
+ controller_name = "Interchange::#{model_class.to_s.pluralize}Controller"
86
+ controller = controller_name.constantize
87
+ @rendering_controller = controller.new
88
+
89
+ # Create dummy request and response objects on the rendering controller,
90
+ # otherwise it will complain. This was gleaned from: http://stackoverflow.com/a/6773022/535054
91
+ @rendering_controller.request = ActionDispatch::TestRequest.new
92
+ @rendering_controller.response = ActionDispatch::TestResponse.new
93
+
94
+ # Retrieve instances of the given model, and then iterate through them to do the export:
95
+ models =
96
+ if ids
97
+ # We have a specific list of ID values we've been asked to export:
98
+ model_class.where(:id => ids)
99
+ elsif keywords
100
+ # We want only those instances that have one of the list of keywords given to us:
101
+ model_class.joins(:metadata_keywords).where(:metadata_keywords => {:text => keywords})
102
+ else
103
+ # We have no specific list of IDs, so we're going to export all instances:
104
+ model_class.find(:all)
105
+ end
106
+
107
+ # Make the call to create_file() for each model instance we've determined needs to be exported:
108
+ models.each do |model|
109
+ begin
110
+ # TODO: Ensure this is a top-level resource before exporting
111
+ create_file model, options
112
+ @operation_results[:successful] << model
113
+ rescue Exception => exception
114
+ @operation_results[:failures] << model
115
+ model.export_errors << "Failure details: #{exception}"
116
+ end
117
+ end
118
+ end
119
+
120
+
121
+ # Internal: Decorates the passed in pretty JSON if needed. Wwe adorn the
122
+ # output with JavaScript that provides comments and variable instantiation.
123
+ #
124
+ # model - ActiveRecord::Base the root model being exported, from which we can get class
125
+ # and name information to decorate with.
126
+ # pretty_json - String with contents ready to be exported, that we'll decorate.
127
+ # options - Hash of options. (default: {})
128
+ #
129
+ # Returns String of the decorated output.
130
+ def decorate_output(model, pretty_json, options={})
131
+ decorated_output = "// #{model.interchange_name}.js\n"
132
+ decorated_output.concat "var CERN#{model.interchange_type_name}_#{model.interchange_name} = #{pretty_json};"
133
+
134
+ decorated_output
135
+ end
136
+
137
+
138
+ # Internal: Creates a local file that contains the provided JSON payload for the given model object.
139
+ # Model objects of a similar class are co-located in their own sub-directory.
140
+ #
141
+ # model - ActiveRecord::Base the root model to be exported.
142
+ # options - Hash of options. (default: {})
143
+ #
144
+ # Returns nothing.
145
+ def create_file(model, options={})
146
+ Rails.logger.debug "Performing local export of #{model.class} model: #{model.short_title}"
147
+ model.export_errors = []
148
+ pretty_json = @rendering_controller.render_show_template_to_string(model, options)
149
+ data_file_contents = decorate_output(model, pretty_json, options)
150
+ base_path = options[:base_path] || 'public/interchange/'
151
+
152
+ interchange_extension = 'js'
153
+ interchange_type = if base_path == 'public/interchange/'
154
+ 'export/'
155
+ else
156
+ ''
157
+ end
158
+
159
+ # Determine directory we will write the file into:
160
+ directory = "#{base_path}#{interchange_type}#{model.class.to_s.pluralize}"
161
+
162
+ # Ensure the directory exists:
163
+ directory_absolute_path = File.join(Rails.root, directory)
164
+ Rails.logger.debug "Absolute directory path to write to: #{directory_absolute_path}"
165
+ system("mkdir -p #{directory_absolute_path}") unless File.exists?(directory_absolute_path)
166
+
167
+ # Write the file to disk:
168
+ file_absolute_path = "#{directory_absolute_path}/#{model.interchange_name}.#{interchange_extension}"
169
+ Rails.logger.debug "Absolute model file path to write to: #{file_absolute_path}"
170
+ file = File.new(file_absolute_path, "w")
171
+ file.write(data_file_contents)
172
+ file.close
173
+ end
174
+
175
+ end # class
176
+ end # module
@@ -0,0 +1,197 @@
1
+ # This module deals with importing and exporting model data. We are a counterpart
2
+ # to controllers under the same Interchange module.
3
+ module Interchange
4
+
5
+ # Handles reading files from the local file system meant for import. Ultimately, we'll pass a JSON object
6
+ # to the model whose data we've loaded, and that model will be responsible for creating a new instance of itself
7
+ # and all of its constituent, wholly owned, related objects.
8
+ #
9
+ # You use mark_import_operation() with a block in which you call import() one or more times, to perform your
10
+ # import operation.
11
+ class Reader
12
+ # Mixins
13
+ include ::Mixins::Logging
14
+ include ::ActionView::Helpers::TextHelper # for pluralize
15
+
16
+ attr_accessor :operation_results
17
+
18
+ # Prepares for an import operation and cleans up after it, summarizing the results.
19
+ # Callers must provide a block that makes one or more calls to our import() method to import
20
+ # various models.
21
+ def mark_import_operation
22
+ # Clear the class cached last-import summary:
23
+ self.class.instance_variable_set(:@last_import_summary, nil)
24
+
25
+ # Create a hash we'll return as the result of the import
26
+ @operation_results = { successful: [], failures: [], skipped: [] }
27
+
28
+ yield # Hand over control to the block that calls our import() method one or more times
29
+
30
+ # Now mark the operation as complete:
31
+ summarize_operation
32
+ self.class.instance_variable_set(:@last_import_summary, @operation_results)
33
+ end
34
+
35
+
36
+ # We kick off the import process for all records in the import folder that belong to the provided model class.
37
+ # If not provided, we default to no clean up of files and the skipping of any duplicates.
38
+ #
39
+ # @param model_class The actual top-level model class whose json files we'll look for and import.
40
+ # @param cleanup Defaults to false. If true, this instructs the successful import operations to be
41
+ # followed up with a deletion of the source file for import.
42
+ # @param duplicate_strategy Defaults to skipping duplicates. Provide a value in module Enums::DuplicateStrategy.
43
+ # @return A hash that summarizes the operation. Included within is another hash that can be used for the Flash
44
+ # that provides an alert or notice summary, as appropriate.
45
+ def import(model_class, cleanup=false, duplicate_strategy=Enums::DuplicateStrategy::SKIP, base_path='public/interchange/import/')
46
+ log "Importing files for model class: #{model_class}, with options" \
47
+ "cleanup: #{cleanup}, duplicate_strategy: #{duplicate_strategy}, base_path: #{base_path}"
48
+
49
+ @model_class = model_class
50
+ @cleanup_requested = cleanup
51
+
52
+ log "Started import for model: #{@model_class.to_s}"
53
+
54
+ # Determine directory we will read files from:
55
+ directory = "#{base_path}#{@model_class.to_s.pluralize}"
56
+
57
+ # Ensure the directory exists:
58
+ models_path = File.join(Rails.root, directory)
59
+ log "Models path to read from: #{models_path}"
60
+
61
+ # Does the directory exist that files of the given model class would be found at?
62
+ if File.exists?(models_path)
63
+ log "Import path '#{models_path}' exists."
64
+ Dir.chdir models_path
65
+ filenames = Dir.glob("*.{js,json}") # The candidate files to import will all have a .json or .js extension.
66
+ log "Matched the following file paths: #{filenames}"
67
+
68
+ # Iterate through all the files in the model directory, and attempt to import each one:
69
+ filenames.each do | filename |
70
+ log "#{self.class.name} import(): Processing file '#{filename}'"
71
+ contents = File.read(filename)
72
+ massaged_contents = transform_javascript_format(contents)
73
+ model = @model_class.send :import, massaged_contents, duplicate_strategy
74
+
75
+ if model.skip_import?
76
+ log "#{self.class.name} import(): Skipping import of file '#{filename}'"
77
+ process_skipped_import(model, filename)
78
+ elsif model.import_errors.empty?
79
+ log "#{self.class.name} import(): Success at import of file '#{filename}'"
80
+ process_model_save(model, filename)
81
+ else
82
+ log "#{self.class.name} import(): Import failed for file '#{filename}'"
83
+ process_failed_import(model)
84
+ end
85
+ end
86
+ else
87
+ log "No import folder exists at path: #{models_path}"
88
+ end
89
+
90
+ end # import
91
+
92
+
93
+ # This takes a file with camel case keys and makes them underscore keys, recognizable
94
+ # to our Ruby models. Additionally, JavaScript commands that encase the core payload,
95
+ # are stripped out, so that we have valid JSON we can parse.
96
+ #
97
+ # Doing this whole exercise lets us export to JavaScript files useful for UIAutomation,
98
+ # while having a single format we can also import for ourselves (Mockingjay).
99
+ def transform_javascript_format(contents)
100
+ # Find positions for the start and end braces:
101
+ start_position = contents.index('{')
102
+ end_position = contents.rindex('}')
103
+
104
+ # Retrieve the string contents of the file between these braces, inclusive:
105
+ massaged_contents = contents[start_position..end_position]
106
+
107
+ # Parse the contents into JSON, and the make all the keys underscore style:
108
+ JSON.parse(massaged_contents).underscore_keys
109
+ end
110
+
111
+
112
+ protected
113
+
114
+ # Records that the import attempt for the given model, failed. We don't delete the source file in this case,
115
+ # regardless of the value of @cleanup_requested, because the user would likely want to inspect the file,
116
+ # retry after adjustments, etc.
117
+ #
118
+ # @param model The model instance that failed to import. This should have at least one entry in its
119
+ # import_errors array.
120
+ def process_failed_import(model)
121
+ @operation_results[:failures] << model
122
+ log "Reader.import(): ERROR. Could not save imported model #{model.short_title}." \
123
+ "Errors: #{model.errors.full_messages.join(' | ')}"
124
+ # Note: for failures, we obviously do not want to delete the source file; so that it might be inspected.
125
+ end
126
+
127
+
128
+ # Attempts to save the provided model. Records a successful import if it succeeds, and a failure if it doesn't.
129
+ # @param model The model instance we will attempt to save.
130
+ # @param filename The source filename (fully qualified) that the model was built from. We'll use this to know
131
+ # what file to delete if the save was successful and cleanup was requested.
132
+ def process_model_save(model, filename)
133
+ log "#{self.class.name} process_model_save(): Entered method."
134
+ begin
135
+ if model.valid?
136
+ log "#{self.class.name} process_model_save(): model is: #{model}"
137
+ #Rails.application.eager_load! # Needed to ensure all models are loaded for our asking them if they are root resources.
138
+ model.save
139
+ @operation_results[:successful] << model
140
+ log "Reader.import(): Successfully saved imported model #{model.model_qualified_title}"
141
+ # Delete the source file, if cleanup was requested:
142
+ File.delete(filename) if @cleanup_requested
143
+ else
144
+ @operation_results[:failures] << model
145
+ log_error "Reader.import(): Error saving model #{model.model_qualified_title}. Details: #{model.errors.full_messages}"
146
+ model.import_errors << "Save failed with error: #{model.errors.full_messages}"
147
+ end
148
+ rescue Exception => e
149
+ log "#{self.class.name} process_model_save(): Exception: #{e}"
150
+ end
151
+ end
152
+
153
+
154
+ # Records that we skipped importing a model, because it was a duplicate.
155
+ # @param model The model instance that we have skipped an import of, because it would duplicate one or more
156
+ # records already in the system.
157
+ # @param filename The source filename (fully qualified) that the model was built from. We'll use this to know
158
+ # what file to delete if cleanup was requested.
159
+ def process_skipped_import(model, filename)
160
+ @operation_results[:skipped] << model
161
+ log "Reader.import(): Skipped importing model #{model.model_qualified_title}"
162
+ # Delete the source file, if cleanup was requested:
163
+ File.delete(filename) if @cleanup_requested
164
+ end
165
+
166
+
167
+ # Adorns the summary hash passed in, beyond its core arrays of successful model import and failed model imports,
168
+ # with keys describing other aspects of the operation, including:
169
+ # * has_failures => Boolean on whether any failures exist
170
+ # * total_records => Sum total of successful and failed model imports; the total count of models we attempted to import
171
+ # * flash_hash => A hash that can be used for the flash messaging that summarizes the status of this import
172
+ def summarize_operation
173
+ message = ''
174
+
175
+ # Failures:
176
+ if @operation_results[:failures].empty?
177
+ flash_key = :notice
178
+ @operation_results[:has_failures] = false
179
+ else
180
+ flash_key = :alert
181
+ @operation_results[:has_failures] = true
182
+ message = "#{@operation_results[:failures].count} record(s) failed to import. "
183
+ end
184
+
185
+ # Skipped:
186
+ @operation_results[:has_skips] = !@operation_results[:skipped].empty?
187
+
188
+ # Summary Info:
189
+ @operation_results[:total_records] = @operation_results[:failures].count + @operation_results[:successful].count + @operation_results[:skipped].count
190
+ message.concat(pluralize(@operation_results[:successful].count, "record")).concat(' imported successfully. ')
191
+ message.concat(pluralize(@operation_results[:skipped].count, "record")).concat(' skipped. ')
192
+ @operation_results[:flash_hash] = { flash_key => message }
193
+ @operation_results[:timestamp] = Time.now
194
+ end
195
+
196
+ end # class
197
+ end # module
@@ -0,0 +1,36 @@
1
+ module Mixins::Logging
2
+
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ end
7
+
8
+ # ---------- Instance Methods ----------
9
+
10
+ # Convenience wrapper for Rails.logger.debug
11
+ def log(message)
12
+ log_debug message
13
+ end
14
+
15
+ # An alias for the more abbreviated log() method,
16
+ # for consistency.
17
+ def log_debug(message)
18
+ Rails.logger.debug message
19
+ end
20
+
21
+
22
+ def log_info(message)
23
+ Rails.logger.info message
24
+ end
25
+
26
+
27
+ def log_warn(message)
28
+ Rails.logger.warn message
29
+ end
30
+
31
+
32
+ def log_error(message)
33
+ Rails.logger.error message
34
+ end
35
+
36
+ end # module Logging
@@ -0,0 +1,31 @@
1
+ # Public: This is where you invoke the load command for each reference data type that piggybacks off of the
2
+ # DataList and DataOption models.
3
+ #
4
+ # Any reference data model with nested reference data structure, is responsible for loading those nested
5
+ # data lists and options.
6
+ #
7
+ # Relies on the constant ROOT_REFERENCE_MODEL_CLASSES, defined in config/initializers/reference_data_types
8
+ class Reference::DataTypes
9
+
10
+ # Loads all reference data classes with information from DataSets marked active.
11
+ def self.load
12
+ Rails.logger.info "Reference::DataTypes load(): Loading reference data types."
13
+ Reference::ROOT_REFERENCE_MODEL_CLASSES.each { |reference_class| reference_class.send(:load_reference_data)}
14
+ end
15
+
16
+
17
+ # De-registers all cached instances of reference data from each reference data type.
18
+ def self.unload
19
+ Rails.logger.info "Reference::DataTypes unload(): Unloading reference data types."
20
+ Reference::ROOT_REFERENCE_MODEL_CLASSES.each { |reference_class| reference_class.send(:unload_reference_data)}
21
+ end
22
+
23
+
24
+ # Reloads our reference data. Handy if a data list or data set has changed.
25
+ def self.reload
26
+ Rails.logger.info "Reference::DataTypes reload(): Reloading reference data types."
27
+ self.unload
28
+ self.load
29
+ end
30
+
31
+ end # class
@@ -0,0 +1,46 @@
1
+ module Reference
2
+ # Public: This class handles the import of data located in the 'data/reference/' folder.
3
+ #
4
+ # We seed the database with default values for reference data.
5
+ class Loader
6
+
7
+ # Mixins
8
+ include ::Mixins::Logging
9
+
10
+ # Internal: Creates our instance of Reader, that handles the actual import.
11
+ def initialize
12
+ @reader = Interchange::Reader.new
13
+ end
14
+
15
+
16
+ # Public: The primary interface to this class. We setup to load all reference data in the
17
+ # data/reference/ folder, such that we leave the seed files intact, do not destroy existing data prior,
18
+ # and that we just replace any imports for data that already exists based on each model's determination
19
+ # of uniqueness.
20
+ def perform_import
21
+ log "#{self.class.name} perform_import(): Began import process."
22
+ # Set the parameters guiding how the import should be run:
23
+ cleanup = false
24
+ duplicate_strategy = Enums::DuplicateStrategy::REPLACE
25
+
26
+ # Wrap the import process in a block that can log the overall status:
27
+ @reader.mark_import_operation do
28
+ # Kick off the underlying import operation on the model(s) requested:
29
+ root_model_classes.each do |model_class|
30
+ puts "Reference::Loader - Loading reference data (if any) for model: #{model_class.to_s}"
31
+ @reader.import model_class, cleanup, duplicate_strategy, Reference::BASE_PATH
32
+ end
33
+ end
34
+ end
35
+
36
+
37
+ private
38
+
39
+ # Determines which of our models are root resources. Only these attempt to get loaded.
40
+ def root_model_classes
41
+ [DataSet]
42
+ end
43
+
44
+ end # class
45
+
46
+ end # module
@@ -0,0 +1,23 @@
1
+ module Reference
2
+
3
+ # Public: Writes reference data back to the file system, which will automatically get loaded next time.
4
+ # This will also get flagged in source control as an update that others will receive, who are running locally.
5
+ class Updater
6
+
7
+ # This constructor sets up Writer class that we offload the low level export work to.
8
+ def initialize
9
+ @writer = Interchange::Writer.new
10
+ end
11
+
12
+
13
+ def perform_export
14
+ # Start the export operation block:
15
+ @writer.mark_export_operation do
16
+ # Kick off the underlying export operation on the reference data model requested:
17
+ @writer.export DataSet, base_path: Reference::BASE_PATH, suppress_database_ids: true
18
+ end
19
+ end
20
+
21
+ end # class: Updater
22
+
23
+ end # module: Reference
@@ -0,0 +1,46 @@
1
+ # This helper class is meant to get yielded to a block of code in a show template.
2
+ # We hold the model object whose attributes are to be displayed, and handle substituting
3
+ # values into a partial template, abstracting HTML boilerplate from the resource's
4
+ # show template itself.
5
+ #
6
+ # We are very similar to the form that gets yielded by a form_for / simple_form_for call
7
+ # except that we are making a show template easier to build, instead of a form.
8
+ #
9
+ class ResourceDisplayHelper
10
+
11
+ # Attributes
12
+ attr_accessor :model_object
13
+ attr_accessor :resource_helper
14
+
15
+
16
+ # ---------- Initializers ----------
17
+
18
+ # Provide the model object we'll be invoking attribute method calls on, as well as the
19
+ # resource helper that we can ask to render a view partial for us.
20
+ def initialize(model_object, resource_helper)
21
+ @model_object = model_object
22
+ @resource_helper = resource_helper
23
+ end
24
+
25
+
26
+ # ---------- Interface ----------
27
+
28
+ # This is the main method of this class that callers will use. They provide
29
+ # the attribute whose label and value is to be displayed. Optional overrides
30
+ # for the label and value are also allowed.
31
+ def attribute(name, options={})
32
+ label = options[:label] || name.to_s.humanize.titleize
33
+ value = options[:value] || model_object.send(name)
34
+ link_path = options[:link_path]
35
+
36
+ if value && value.kind_of?(String) && options[:truncate]
37
+ value = value.truncate(options[:truncate], :omission => "...", :separator => ' ')
38
+ end
39
+
40
+ # We don't have access to the view renderer, but our provider resource helper does.
41
+ # Use it to render the partial that holds the boilerplate template for how we like
42
+ # to show a single attribute:
43
+ resource_helper.render partial: 'mock/common/show_attribute', locals: {label: label, value: value, link_path: link_path}
44
+ end
45
+
46
+ end # class
@@ -0,0 +1,20 @@
1
+ # NOTE: only doing this in development as some production environments (Heroku)
2
+ # NOTE: are sensitive to local FS writes, and besides -- it's just not proper
3
+ # NOTE: to have a dev-mode tool do its thing in production.
4
+ if(Rails.env.development?)
5
+ task :set_annotation_options do
6
+ ENV['position_in_class'] = "before"
7
+ ENV['position_in_fixture'] = "before"
8
+ ENV['position_in_factory'] = "before"
9
+ ENV['show_indexes'] = "true"
10
+ ENV['include_version'] = "false"
11
+ ENV['exclude_tests'] = "false"
12
+ ENV['exclude_fixtures'] = "false"
13
+ ENV['ignore_model_sub_dir'] = "false"
14
+ ENV['skip_on_db_migrate'] = "false"
15
+ ENV['format_rdoc'] = "false"
16
+ ENV['format_markdown'] = "false"
17
+ ENV['no_sort'] = "false"
18
+ ENV['force'] = "false"
19
+ end
20
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ion_orders_engine_mockingjay
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1.SNAPSHOT
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Schuster
@@ -120,6 +120,10 @@ files:
120
120
  - app/helpers/reference/data_elements_helper.rb
121
121
  - app/helpers/reference/data_lists_helper.rb
122
122
  - app/helpers/reference/data_sets_helper.rb
123
+ - app/models/concerns/mock/import_concern.rb
124
+ - app/models/concerns/mock/phone_number_concern.rb
125
+ - app/models/concerns/mock/resource_ancestors_concern.rb
126
+ - app/models/concerns/mock/root_model_concern.rb
123
127
  - app/views/interchange/common/_database_info.json.jbuilder
124
128
  - app/views/interchange/common/_metadata_keywords.json.jbuilder
125
129
  - app/views/interchange/common/_test_assertions.json.jbuilder
@@ -180,11 +184,23 @@ files:
180
184
  - config/locales/simple_form.en.yml
181
185
  - config/mongo.yml
182
186
  - config/routes.rb
187
+ - db/development.sqlite3
183
188
  - db/schema.rb
184
189
  - db/seeds.rb
190
+ - lib/bootstrap_breadcrumbs_builder.rb
191
+ - lib/enums/duplicate_strategy.rb
192
+ - lib/enums.rb
193
+ - lib/interchange/reader.rb
194
+ - lib/interchange/Writer.rb
185
195
  - lib/ion_orders_engine_mockingjay/engine.rb
186
196
  - lib/ion_orders_engine_mockingjay/version.rb
187
197
  - lib/ion_orders_engine_mockingjay.rb
198
+ - lib/mixins/logging.rb
199
+ - lib/reference/data_types.rb
200
+ - lib/reference/loader.rb
201
+ - lib/reference/updater.rb
202
+ - lib/resource_display_helper.rb
203
+ - lib/tasks/auto_annotate_models.rake
188
204
  - lib/tasks/ion_orders_engine_mockingjay_tasks.rake
189
205
  - Rakefile
190
206
  - README.md
@@ -202,9 +218,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
202
218
  version: '0'
203
219
  required_rubygems_version: !ruby/object:Gem::Requirement
204
220
  requirements:
205
- - - '>'
221
+ - - '>='
206
222
  - !ruby/object:Gem::Version
207
- version: 1.3.1
223
+ version: '0'
208
224
  requirements: []
209
225
  rubyforge_project:
210
226
  rubygems_version: 2.1.4