Class: NcsNavigator::Mdes::Variable

Inherits:
Object
  • Object
show all
Defined in:
lib/ncs_navigator/mdes/variable.rb

Overview

A single field in the MDES. A relational model might also call this a column, but "variable" is what it's called in the MDES.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ Variable

Returns a new instance of Variable.



129
130
131
# File 'lib/ncs_navigator/mdes/variable.rb', line 129

def initialize(name)
  @name = name
end

Instance Attribute Details

#nameString (readonly)

Returns the name of the variable.

Returns:

  • (String)

    the name of the variable



10
11
12
# File 'lib/ncs_navigator/mdes/variable.rb', line 10

def name
  @name
end

#nillableBoolean Also known as: nillable?

May the variable be submitted as a null value?

Returns:

  • (Boolean)


45
46
47
# File 'lib/ncs_navigator/mdes/variable.rb', line 45

def nillable
  @nillable
end

#omittableBoolean Also known as: omittable?

If the variable does not have a value, should it be completely omitted when submitting to the VDR?

Returns:

  • (Boolean)


38
39
40
# File 'lib/ncs_navigator/mdes/variable.rb', line 38

def omittable
  @omittable
end

#piiBoolean, ...

Returns the PII category for the variable. true if it is definitely PII, false if it definitely is not, :possible if it was marked in the MDES as requiring manual review, :unknown if the MDES does not specify, or a string if the parsed value was not mappable to one of the above. Note that this value will always be truthy unless the MDES explicitly says that the variable is not PII.

Returns:

  • (Boolean, :possible, :unknown, String)

    the PII category for the variable. true if it is definitely PII, false if it definitely is not, :possible if it was marked in the MDES as requiring manual review, :unknown if the MDES does not specify, or a string if the parsed value was not mappable to one of the above. Note that this value will always be truthy unless the MDES explicitly says that the variable is not PII.



20
21
22
# File 'lib/ncs_navigator/mdes/variable.rb', line 20

def pii
  @pii
end

#required=(value) ⇒ Boolean? (writeonly)

Allows for an override of the default logic. Mostly intended for testing. Set to nil to restore default logic.

Returns:

  • (Boolean, nil)


53
54
55
# File 'lib/ncs_navigator/mdes/variable.rb', line 53

def required=(value)
  @required = value
end

#status:active, ...

Returns the status of the variable in this version of the MDES. A String is returned if the source value doesn't match any of the expected values in the MDES.

Returns:

  • (:active, :new, :modified, :retired, String)

    the status of the variable in this version of the MDES. A String is returned if the source value doesn't match any of the expected values in the MDES.



27
28
29
# File 'lib/ncs_navigator/mdes/variable.rb', line 27

def status
  @status
end

#table_referenceTransmissionTable?

If this variable is a foreign key, this is the table to which it refers.

Returns:



60
61
62
# File 'lib/ncs_navigator/mdes/variable.rb', line 60

def table_reference
  @table_reference
end

#typeVariableType

Returns the type of this variable.

Returns:



31
32
33
# File 'lib/ncs_navigator/mdes/variable.rb', line 31

def type
  @type
end

Class Method Details

.deep_key_or_nil(h, *keys) ⇒ Object



118
119
120
121
122
123
124
125
# File 'lib/ncs_navigator/mdes/variable.rb', line 118

def deep_key_or_nil(h, *keys)
  value = h[keys.shift]
  if value.respond_to?(:[]) && !keys.empty?
    deep_key_or_nil(value, *keys)
  else
    value
  end
end

.from_element(element, options = {}) ⇒ Variable

Examines the given parsed element and creates a new variable. The resulting variable has all the attributes set which can be set without reference to any other parts of the MDES outside of this one variable definition.

Parameters:

  • element (Nokogiri::Element)

    the source xs:element

Returns:

  • (Variable)

    a new variable instance



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/ncs_navigator/mdes/variable.rb', line 71

def from_element(element, options={})
  log = options[:log] || NcsNavigator::Mdes.default_logger

  new(element['name']).tap do |var|
    var.nillable = element['nillable'] == 'true'
    var.omittable = element['minOccurs'] == '0'
    var.pii =
      case element['ncsdoc:pii']
      when 'Y'; true;
      when 'P'; :possible;
      when nil; :unknown;
      when '';  false;
      else element['ncsdoc:pii'];
      end
    var.status =
      case element['ncsdoc:status']
      when '1'; :active;
      when '2'; :new;
      when '3'; :modified;
      when '4'; :retired;
      else element['ncsdoc:status'];
      end
    var.type = type_for(element, var.name, log, options)
  end
end

.type_for(element, name, log, options) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/ncs_navigator/mdes/variable.rb', line 97

def type_for(element, name, log, options)
  variable_type_override = deep_key_or_nil(
    options, :heuristic_overrides, 'variable_type_references', options[:current_table_name], name)

  named_type = variable_type_override || element['type']

  if named_type
    if named_type =~ /^xs:/
      VariableType.xml_schema_type(named_type.sub(/^xs:/, ''))
    else
      VariableType.reference(named_type)
    end
  elsif element.elements.collect { |e| e.name } == %w(simpleType)
    VariableType.from_xsd_simple_type(element.elements.first, options)
  else
    log.warn("Could not determine a type for variable #{name.inspect} on line #{element.line}")
    nil
  end
end

Instance Method Details

#diff(other_variable, options = {}) ⇒ Differences::Entry?

Computes the differences between this variable and the other.

Returns:



278
279
280
# File 'lib/ncs_navigator/mdes/variable.rb', line 278

def diff(other_variable, options={})
  Differences::Entry.compute(self, other_variable, diff_criteria(options), options)
end

#required?Boolean Also known as: required

Is a value for the variable mandatory for a valid submission?

Returns:

  • (Boolean)


137
138
139
140
141
142
143
# File 'lib/ncs_navigator/mdes/variable.rb', line 137

def required?
  if @required.nil?
    !(omittable? || nillable?)
  else
    @required
  end
end

#resolve_foreign_key!(tables, override_name = nil, options = {})

This method returns an undefined value.

Attempts to resolve this variable as a reference to another table. There are two mechanisms for performing the resolution:

  1. If an override_name is provided, that table will be looked up in the provided table list. If it exists, it will be used, otherwise nothing will be set.
  2. If the type of this variable is one of the NCS FK types and there exists exactly one table in tables whose primary key has the same name as this variable, that table will be used.

Alternatively, to suppress the heuristics without providing a replacement, pass false as the override_name

Parameters:

  • tables (Array<TransmissionTable>)

    the tables to search for a parent.

  • override_name (String, nil) (defaults to: nil)

    the name of a table to use as the parent. Supplying a value in this parameter bypasses the search heuristic.



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/ncs_navigator/mdes/variable.rb', line 191

def resolve_foreign_key!(tables, override_name=nil, options={})
  log = options[:log] || NcsNavigator::Mdes.default_logger
  source_table = options[:in_table] ? options[:in_table].name : '[unspecified table]'

  case override_name
  when String
    self.table_reference = tables.detect { |t| t.name == override_name }
    unless table_reference
      log.warn("Foreign key #{name.inspect} in #{source_table} explicitly mapped " <<
        "to unknown table #{override_name.inspect}.")
    end
  when nil
    return unless (self.type && self.type.name =~ /^foreignKey/)

    candidates = tables.select do |t|
      t.variables.detect { |v| (v.name == name) && (v.type && (v.type.name =~ /^primaryKey/)) }
    end

    case candidates.size
    when 0
      log.warn("Foreign key in #{source_table} not resolvable: " <<
        "no tables have a primary key named #{name.inspect}.")
    when 1
      self.table_reference = candidates.first
    else
      log.warn(
        "#{candidates.size} possible parent tables found for foreign key #{name.inspect} " <<
        "in #{source_table}: #{candidates.collect { |c| c.name.inspect }.join(', ')}. " <<
        "None used due to ambiguity.")
    end
  end
end

#resolve_type!(types, options = {})

This method returns an undefined value.

If the #type of this instance is a reference to an NCS type, attempts to replace it with the full version from the given list of types.

Parameters:



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/ncs_navigator/mdes/variable.rb', line 153

def resolve_type!(types, options={})
  log = options[:log] || NcsNavigator::Mdes.default_logger

  return unless type && type.reference?
  unless type.name =~ /^ncs:/
    log.warn("Unknown reference namespace in type #{type.name.inspect} for #{name}")
  end

  ncs_type_name = type.name.sub(/^ncs:/, '')
  match = types.find { |t| t.name == ncs_type_name }
  if match
    self.type = match
  else
    log.warn("Undefined type #{type.name} for #{name}.") if log
  end
end