ion_orders_engine_mockingjay 1.0.1.SNAPSHOT → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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