diff --git a/lib/stupidedi/versions/common/element_types/an.rb b/lib/stupidedi/versions/common/element_types/an.rb index f855d9e99..ca35b89f9 100644 --- a/lib/stupidedi/versions/common/element_types/an.rb +++ b/lib/stupidedi/versions/common/element_types/an.rb @@ -143,13 +143,6 @@ def too_short? false end - # - # Objects passed to StringVal.value that don't respond to #to_s are - # modeled by this class. Note most everything in Ruby responds to - # that method, including things that really shouldn't be considered - # StringVals (like Array or Class), so other validation should be - # performed on StringVal::NonEmpty values. - # class Invalid < StringVal # @return [Object] attr_reader :value @@ -167,12 +160,13 @@ def empty? false end - # @return [StringVal] + # @return [Invalid] def map - StringVal.value(yield(nil), usage, position) + self end # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -188,6 +182,7 @@ def inspect ansi.element("AN.invalid#{id}") + "(#{ansi.invalid(@value.inspect)})" end + # :nocov: # @return [String] def to_s @@ -201,7 +196,7 @@ def to_x12(truncate = true) # @return [Boolean] def ==(other) - eql?(other) or other.nil? + eql?(other) end # @return [Invalid] @@ -210,43 +205,43 @@ def copy(changes = {}) end end - # - # Empty string value. Shouldn't be directly instantiated -- instead, - # use the {StringVal.empty} constructor. - # - class Empty < StringVal + class Valid < StringVal include Comparable - # (string any* -> any) def_delegators :value, :to_d, :to_s, :to_f, :to_c, :to_r, :to_sym, :to_str, :hex, :oct, :ord, :sum, :length, :count, :index, :rindex, :lines, :bytes, :chars, :each, :upto, :split, :scan, :unpack, :=~, :match, :partition, :rpatition, :encoding, :valid_enocding?, :at, :empty?, :blank? - # (string any* -> StringVal) extend Operators::Wrappers wrappers :%, :+, :*, :slice, :take, :drop, :[], :capitalize, :center, :ljust, :rjust, :chomp, :delete, :tr, :tr_s, :sub, :gsub, :encode, :force_encoding, :squeeze - # (string -> StringVal) extend Operators::Unary unary_operators :chr, :chop, :upcase, :downcase, :strip, :lstrip, :rstrip, :dump, :succ, :next, :reverse, :swapcase - # (string string -> any) extend Operators::Relational - relational_operators :==, :<=>, :start_with?, :end_with?, - :include?, :casecmp, :coerce => :to_s + relational_operators :<=>, :start_with?, :end_with?, + :include?, :casecmp, :coerce => :to_str + + def ==(other) + other = StringVal.value(other, usage, position) + other.valid? and other.value == value + end - # @return [Empty] + # @return [StringVal] def copy(changes = {}) - self + StringVal.value \ + changes.fetch(:value, value), + changes.fetch(:usage, usage), + changes.fetch(:position, position) end - def value - "" + def coerce(other) + return StringVal.value(other, usage, position), self end def valid? @@ -255,10 +250,18 @@ def valid? # @return [StringVal] def map - StringVal.value(yield(nil), usage, position) + StringVal.value(yield(value), usage, position) + end + end + + class Empty < Valid + # @return [String] + def value + "" end # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -274,59 +277,27 @@ def inspect ansi.element("AN.empty#{id}") end + # :nocov: # @return [String] def to_x12(truncate = true) "" end - end - # - # Non-empty string value. Shouldn't be directly instantiated -- - # instead, use the {StringVal.value} constructor. - # - class NonEmpty < StringVal - include Comparable + def to_date(format) + nil + end + end + class NonEmpty < Valid # @return [String] attr_reader :value - # (string any* -> any) - def_delegators :@value, :to_d, :to_s, :to_f, :to_c, :to_r, :to_sym, - :to_str, :hex, :oct, :ord, :sum, :length, :count, :index, :rindex, - :lines, :bytes, :chars, :each, :upto, :split, :scan, :unpack, :=~, - :match, :partition, :rpatition, :encoding, :valid_enocding?, :at, - :empty?, :blank? - - # (string any* -> StringVal) - extend Operators::Wrappers - wrappers :%, :+, :*, :slice, :take, :drop, :[], :capitalize, - :center, :ljust, :rjust, :chomp, :delete, :tr, :tr_s, - :sub, :gsub, :encode, :force_encoding, :squeeze - - # (string -> StringVal) - extend Operators::Unary - unary_operators :chr, :chop, :upcase, :downcase, :strip, - :lstrip, :rstrip, :dump, :succ, :next, :reverse, :swapcase - - # (string string -> any) - extend Operators::Relational - relational_operators :==, :<=>, :start_with?, :end_with?, - :include?, :casecmp, :coerce => :to_s - def initialize(string, usage, position) @value = string super(usage, position) end - # @return [StringVal] - def copy(changes = {}) - StringVal.value \ - changes.fetch(:value, @value), - changes.fetch(:usage, usage), - changes.fetch(:position, position) - end - def too_long? @value.lstrip.length > definition.max_length end @@ -335,11 +306,6 @@ def too_short? @value.lstrip.length < definition.min_length end - # @return [StringVal] - def map - StringVal.value(yield(@value), usage, position) - end - # @return [String] def to_x12(truncate = true) x12 = @value.ljust(definition.min_length, " ") @@ -347,6 +313,7 @@ def to_x12(truncate = true) end # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -362,10 +329,7 @@ def inspect ansi.element("AN.value#{id}") + "(#{@value})" end - - def valid? - true - end + # :nocov: # (see AN.stpftime) def to_date(format) @@ -385,7 +349,9 @@ def empty(usage, position) # @return [StringVal] def value(object, usage, position) - if object.blank? + if object.is_a?(StringVal) + object#.copy(:usage => usage, :position => position) + elsif object.blank? self::Empty.new(usage, position) elsif object.kind_of?(Date) or object.kind_of?(Time) self::Invalid.new(object, usage, position) diff --git a/lib/stupidedi/versions/common/element_types/dt.rb b/lib/stupidedi/versions/common/element_types/dt.rb index 13b697d83..fde8f0333 100644 --- a/lib/stupidedi/versions/common/element_types/dt.rb +++ b/lib/stupidedi/versions/common/element_types/dt.rb @@ -74,9 +74,11 @@ def empty? # @return [DateVal] def map - DateVal.value(yield(nil), usage, position) + self end + # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -92,6 +94,7 @@ def inspect ansi.element("DT.invalid#{id}") + "(#{ansi.invalid(@value.inspect)})" end + # :nocov: # @return [String] def to_s @@ -114,25 +117,49 @@ def copy(changes = {}) end end + class Valid < DateVal + def valid? + true + end + + # @return [DateVal] + def map + DateVal.value(yield(value), usage, position) + end + + # @return [DateVal] + def copy(changes = {}) + DateVal.value \ + changes.fetch(:value, value), + changes.fetch(:usage, usage), + changes.fetch(:position, position) + end + + def coerce(other) + return DateVal.value(other, usage, position), self + end + + def ==(other) + other = DateVal.value(other, usage, position) + other.valid? and other.value == value + end + end + # # Empty date value. Shouldn't be directly instantiated -- instead, # use the {DateVal.empty} constructor. # - class Empty < DateVal - def valid? - true + class Empty < Valid + def value + nil end def empty? true end - # @return [DateVal] - def map - DateVal.value(yield(nil), usage, position) - end - # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -148,6 +175,7 @@ def inspect ansi.element("DT.empty#{id}") end + # :nocov: # @return [String] def to_s @@ -158,44 +186,28 @@ def to_s def to_x12(truncate = true) "" end - - # @return [Boolean] - def ==(other) - other.is_a?(Empty) or other.nil? - end - - # @return [Empty] - def copy(changes = {}) - self - end end # # Date with a fully-specified year (with century). Shouldn't be # directly instantiated -- instead use the {DateVal.value} constructor # - class Proper < DateVal - include Comparable - - # (date any* -> any) - def_delegators :@value, :year, :month, :day, :cwday, :cweek, :downto, :upto, - :step, :httpdate, :to_s, :to_i, :strftime, :iso8601, :rfc2822, - :rfc3339, :rfc822, :leap?, :julian?, :gregorian?, :mday, :mon, - :to_datetime, :to_int, :to_r, :to_c, :wday, :xmlschema, :yday, - :start + class Proper < Valid + def_delegators :@value, :year, :month, :day, :cwday, :cweek, + :downto, :upto, :step, :httpdate, :to_s, :to_i, :strftime, + :iso8601, :rfc2822, :rfc3339, :rfc822, :leap?, :julian?, + :gregorian?, :mday, :mon, :to_datetime, :to_int, :to_r, :to_c, + :wday, :xmlschema, :yday, :start - # (date any* -> DateVal::Proper) extend Operators::Wrappers wrappers :+, :<<, :>>, :next_day, :next_month, :next_year, :prev_day, :prev_month, :prev_year - # (date -> DateVal::Proper) extend Operators::Unary unary_operators :next, :succ, :prev - # (date date -> any) extend Operators::Relational - relational_operators :==, :<=>, :-, :coerce => :to_date + relational_operators :<, :>, :<=, :>=, :<=>, :-, :coerce => :to_date attr_reader :value @@ -211,22 +223,6 @@ def initialize(value, usage, position) super(usage, position) end - # @return [Proper] - def copy(changes = {}) - Proper.new \ - changes.fetch(:value, @value), - changes.fetch(:usage, usage), - changes.fetch(:position, position) - end - - def coerce(other) - return DateVal.value(other, usage, position), self - end - - def valid? - true - end - def empty? false end @@ -281,12 +277,8 @@ def past self end - # @return [DateVal] - def map - DateVal.value(yield(@value), usage, position) - end - # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -302,6 +294,7 @@ def inspect ansi.element("DT.value#{id}") + "(#{"%04d-%02d-%02d" % [year, month, day]})" end + # :nocov: # @return [String] def to_x12(truncate = true) @@ -333,7 +326,7 @@ def too_long? # Shouldn't be directly instantiated -- instead, use the constuctor # method {DateVal.value} # - class Improper < DateVal + class Improper < Valid # @return [Integer] attr_reader :year @@ -355,18 +348,18 @@ def initialize(year, month, day, usage, position) super(usage, position) end - # @return [Improper] - def copy(changes = {}) - Improper.new \ - changes.fetch(:year, @year), - changes.fetch(:month, @month), - changes.fetch(:day, @day), - changes.fetch(:usage, usage), - changes.fetch(:position, position) + def value + [@year, @month, @day] end - def valid? - true + def copy(changes = {}) + if [:year, :month, :day].any?{|k| changes.include?(k) } + changes[:value] = [changes.fetch(:year, @year), + changes.fetch(:month, @month), + changes.fetch(:day, @day)] + end + + super(changes) end def empty? @@ -377,6 +370,19 @@ def proper? false end + def too_short? + # Less than a 4-digit year means our max length is 7, but in + # practice the definition min/max lengths are either 6 or 8 + definition.min_length > 6 + end + + def too_long? + # We know month and day occupy four characters, but year *could* + # occupy either three or two characters. If the max_length can't + # accomodate a three-digit year, make sure we don't have one + definition.max_length < 7 and @year > 99 + end + # Create a proper date using the given century `cc` # # @example @@ -465,6 +471,7 @@ def future end # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -480,6 +487,7 @@ def inspect ansi.element("DT.value#{id}") + "(XX#{"%02d-%02d-%02d" % [@year, @month, @day]})" end + # :nocov: # @return [String] def to_s @@ -490,28 +498,6 @@ def to_s def to_x12(truncate = true) "%02d%02d%02d" % [@year, @month, @day] end - - def too_short? - # Less than a 4-digit year means our max length is 7, but in - # practice the definition min/max lengths are either 6 or 8 - definition.min_length > 6 - end - - def too_long? - # We know month and day occupy four characters, but year *could* - # occupy either three or two characters. If the max_length can't - # accomodate a three-digit year, make sure we don't have one - definition.max_length < 7 and @year > 99 - end - - # @note Not commutative - # @return [Boolean] - def ==(other) - eql?(other) or - (@day == other.day and - @year == other.year and - @month == other.month) - end end end @@ -526,7 +512,9 @@ def empty(usage, position) # @return [DateVal] def value(object, usage, position) - if object.blank? + if object.is_a?(DateVal) + object#.copy(:usage => usage, :position => position) + elsif object.blank? self::Empty.new(usage, position) elsif object.is_a?(String) or object.is_a?(StringVal) string = object.to_s @@ -544,10 +532,14 @@ def value(object, usage, position) self::Proper.new(date(year, month, day), usage, position) end end - elsif object.is_a?(DateVal::Improper) - self::Improper.new(object.year, object.month, object.day, usage, position) elsif object.respond_to?(:year) and object.respond_to?(:month) and object.respond_to?(:day) self::Proper.new(date(object.year, object.month, object.day), usage, position) + elsif object.is_a?(Array) and object.length == 3 + if object[0] <= 99 + self::Improper.new(*object, usage, position) + else + self::Proper.new(date(*object), usage, position) + end else self::Invalid.new(object, usage, position) end diff --git a/lib/stupidedi/versions/common/element_types/id.rb b/lib/stupidedi/versions/common/element_types/id.rb index 7fcbf9475..8bf5f5156 100644 --- a/lib/stupidedi/versions/common/element_types/id.rb +++ b/lib/stupidedi/versions/common/element_types/id.rb @@ -74,6 +74,11 @@ def initialize(value, usage, position) super(usage, position) end + # @return [Invalid] + def copy(changes = {}) + self + end + def valid? false end @@ -84,10 +89,11 @@ def empty? # @return [IdentifierVal] def map - IdentifierVal.value(yield(nil), usage, position) + self end # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -103,6 +109,7 @@ def inspect ansi.element("ID.invalid#{id}") + "(#{ansi.invalid(@value.inspect)})" end + # :nocov: # @return [String] def to_s @@ -118,19 +125,9 @@ def to_x12(truncate = true) def ==(other) eql?(other) end - - # @return [Invalid] - def copy(changes = {}) - self - end end - # - # Empty identifier value. Shouldn't be directly instantiated -- - # instead, use the {IdentifierVal.empty} and {IdentifierVal.value} - # constructors - # - class Empty < IdentifierVal + class Valid < IdentifierVal include Comparable # (string any* -> any) @@ -140,7 +137,6 @@ class Empty < IdentifierVal :match, :partition, :rpatition, :encoding, :valid_enocding?, :at, :empty?, :blank? - # (string any* -> StringVal) extend Operators::Wrappers wrappers :%, :+, :*, :slice, :take, :drop, :[], :capitalize, @@ -154,31 +150,44 @@ class Empty < IdentifierVal # (string string -> any) extend Operators::Relational - relational_operators :==, :<=>, :start_with?, :end_with?, - :include?, :casecmp, :coerce => :to_s + relational_operators :<=>, :start_with?, :end_with?, + :include?, :casecmp, :coerce => :to_str - # @return [IdentifierVal] + def ==(other) + other = IdentifierVal.value(other, usage, position) + other.valid? and other.value == value + end + + # @return [StringVal] def copy(changes = {}) - IdentifierVal.value \ + StringVal.value \ changes.fetch(:value, value), changes.fetch(:usage, usage), changes.fetch(:position, position) end - def value - "" + def coerce(other) + return IdentifierVal.value(other, usage, position), self + end + + # @return [IdentifierVal] + def map + IdentifierVal.value(yield(value), usage, position) end def valid? true end + end - # @return [IdentifierVal] - def map - IdentifierVal.value(yield(nil), usage, position) + class Empty < Valid + # @return [String] + def value + "" end # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -194,6 +203,7 @@ def inspect ansi.element("ID.empty#{id}") end + # :nocov: # @return [String] def to_x12(truncate = true) @@ -201,56 +211,15 @@ def to_x12(truncate = true) end end - # - # Non-empty identifier value. Shouldn't be directly instantiated -- - # instead, use the {IdentifierVal.value} constructor - # - class NonEmpty < IdentifierVal - include Comparable - + class NonEmpty < Valid # @return [String] attr_reader :value - # (string any* -> any) - def_delegators :value, :to_d, :to_s, :to_f, :to_c, :to_r, :to_sym, - :to_str, :hex, :oct, :ord, :sum, :length, :count, :index, :rindex, - :lines, :bytes, :chars, :each, :upto, :split, :scan, :unpack, :=~, - :match, :partition, :rpatition, :encoding, :valid_enocding?, :at, - :empty?, :blank? - - - # (string any* -> StringVal) - extend Operators::Wrappers - wrappers :%, :+, :*, :slice, :take, :drop, :[], :capitalize, - :center, :ljust, :rjust, :chomp, :delete, :tr, :tr_s, - :sub, :gsub, :encode, :force_encoding, :squeeze - - # (string -> StringVal) - extend Operators::Unary - unary_operators :chr, :chop, :upcase, :downcase, :strip, - :lstrip, :rstrip, :dump, :succ, :next, :reverse, :swapcase - - # (string string -> any) - extend Operators::Relational - relational_operators :==, :<=>, :start_with?, :end_with?, - :include?, :casecmp, :coerce => :to_s def initialize(value, usage, position) @value = value super(usage, position) end - # @return [IdentifierVal] - def copy(changes = {}) - IdentifierVal.value \ - changes.fetch(:value, @value), - changes.fetch(:usage, usage), - changes.fetch(:position, position) - end - - def valid? - true - end - def too_long? @value.length > definition.max_length end @@ -259,11 +228,6 @@ def too_short? @value.length < definition.min_length end - # @return [IdentifierVal] - def map - IdentifierVal.value(yield(@value), usage, position) - end - # @return [String] def to_x12(truncate = true) x12 = @value.ljust(definition.min_length, " ") @@ -271,6 +235,7 @@ def to_x12(truncate = true) end # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -298,6 +263,7 @@ def inspect ansi.element("ID.value#{id}") + "(#{value})" end + # :nocov: end end @@ -312,7 +278,9 @@ def empty(usage, position) # @return [IdentifierVal] def value(object, usage, position) - if object.blank? + if object.is_a?(IdentifierVal) + object#.copy(:usage => usage, :position => position) + elsif object.blank? self::Empty.new(usage, position) else self::NonEmpty.new(object.to_s.rstrip, usage, position) diff --git a/lib/stupidedi/versions/common/element_types/nn.rb b/lib/stupidedi/versions/common/element_types/nn.rb index b0287ebeb..c6fb0c513 100644 --- a/lib/stupidedi/versions/common/element_types/nn.rb +++ b/lib/stupidedi/versions/common/element_types/nn.rb @@ -72,10 +72,11 @@ def empty? # @return [FixnumVal] def map - FixnumVal.value(yield(nil), usage, position) + self end # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -91,6 +92,7 @@ def inspect ansi.element("Nn.invalid#{id}") + "(#{ansi.invalid(@value.inspect)})" end + # :nocov: # @return [String] def to_s @@ -113,25 +115,49 @@ def copy(changes = {}) end end + class Valid < FixnumVal + def valid? + true + end + + # @return [FixnumVal] + def map + FixnumVal.value(yield(value), usage, position) + end + + # @return [Empty] + def copy(changes = {}) + FixnumVal.value \ + changes.fetch(:value, value), + changes.fetch(:usage, usage), + changes.fetch(:position, position) + end + + def coerce(other) + return FixnumVal.value(other, usage, position), self + end + + def ==(other) + other = FixnumVal.value(other, usage, position) + other.valid? and other.value == value + end + end + # # Empty numeric value. Shouldn't be directly instantiated -- instead # use the {FixnumVal.value} and {FixnumVal.empty} constructors. # - class Empty < FixnumVal - def valid? - true + class Empty < Valid + def value + nil end def empty? true end - # @return [FixnumVal] - def map - FixnumVal.value(yield(nil), usage, position) - end - # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -147,6 +173,7 @@ def inspect ansi.element("Nn.empty#{id}") end + # :nocov: # @return [String] def to_s @@ -157,74 +184,51 @@ def to_s def to_x12(truncate = true) "" end - - # @return [Boolean] - def ==(other) - other.is_a?(Empty) or other.nil? - end - - # @return [Empty] - def copy(changes = {}) - FixnumVal.value \ - changes.fetch(:value, nil), - changes.fetch(:usage, @usage), - changes.fetch(:position, @position) - end end # # Non-empty numeric value. Shouldn't be directly instantiated -- # instead, use the {FixnumVal.value} constructors. # - class NonEmpty < FixnumVal - include Comparable - + class NonEmpty < Valid # @group Mathematical Operators ################################################################# extend Operators::Binary binary_operators :+, :-, :*, :/, :%, :coerce => :to_d - extend Operators::Relational - relational_operators :==, :<=>, :coerce => :to_d - extend Operators::Unary unary_operators :abs, :-@, :+@ + extend Operators::Relational + relational_operators :<, :>, :<=, :>=, :<=>, :coerce => :to_d + # @endgroup ################################################################# # @return [BigDecimal] attr_reader :value - def_delegators :@value, :to_i, :to_d, :to_f, :to_r, :to_c + def_delegators :value, :to_i, :to_d, :to_f, :to_r, :to_c def initialize(value, usage, position) @value = value super(usage, position) end - # @return [NonEmpty] - def copy(changes = {}) - NonEmpty.new \ - changes.fetch(:value, @value), - changes.fetch(:usage, usage), - changes.fetch(:position, position) - end - - def coerce(other) - return copy(:value => other.to_d), self + def empty? + false end - def valid? - true - end + def too_long? + nn = (@value * (10 ** definition.precision)).to_i - def empty? - false + # The length of a numeric type does not include an optional sign + definition.max_length < nn.abs.to_s.length end # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -238,8 +242,9 @@ def inspect end end - ansi.element("Nn.value#{id}") + "(#{to_s})" + ansi.element("Nn.value#{id}") + "(#{value.to_s("F")})" end + # :nocov: # @return [String] def to_s @@ -261,18 +266,6 @@ def to_x12(truncate = true) sign = sign + nn.abs.to_s.rjust(definition.min_length, "0") end end - - def too_long? - nn = (@value * (10 ** definition.precision)).to_i - - # The length of a numeric type does not include an optional sign - definition.max_length < nn.abs.to_s.length - end - - # @return [FixnumVal] - def map - FixnumVal.value(yield(@value), usage, position) - end end end @@ -287,13 +280,16 @@ def empty(usage, position) # @return [FixnumVal] def value(object, usage, position) - if object.blank? + if object.is_a?(FixnumVal) + object#.copy(:usage => usage, :position => position) + elsif object.blank? self::Empty.new(usage, position) - else + elsif object.is_a?(String) # The number of fractional digits is implied by usage.precision factor = 10 ** usage.definition.precision - self::NonEmpty.new(object.to_d / factor, usage, position) + else + self::NonEmpty.new(object.to_d, usage, position) end rescue ArgumentError self::Invalid.new(object, usage, position) diff --git a/lib/stupidedi/versions/common/element_types/r.rb b/lib/stupidedi/versions/common/element_types/r.rb index 36dfa7eac..1772c34b0 100644 --- a/lib/stupidedi/versions/common/element_types/r.rb +++ b/lib/stupidedi/versions/common/element_types/r.rb @@ -72,10 +72,11 @@ def empty? # @return [FloatVal] def map - FloatVal.value(yield(nil), usage, position) + self end # @return [String] + # :nocov: def inspect id = definition.try do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -91,6 +92,7 @@ def inspect ansi.element(" R.invalid#{id}") + "(#{ansi.invalid(@value.inspect)})" end + # :nocov: # @return [String] def to_s @@ -113,25 +115,46 @@ def copy(changes = {}) end end - # - # Empty numeric value. Shouldn't be directly instantiated -- instead - # use the {FloatVal.value} and {FloatVal.empty} constructors. - # - class Empty < FloatVal + class Valid < FloatVal def valid? true end - def empty? - true + def coerce(other) + return FloatVal.value(other, usage, position), self + end + + # @return [Empty] + def copy(changes = {}) + FloatVal.value \ + changes.fetch(:value, value), + changes.fetch(:usage, usage), + changes.fetch(:position, position) end # @return [FloatVal] def map - FloatVal.value(yield(nil), usage, position) + FloatVal.value(yield(value), usage, position) + end + + # @return [Boolean] + def ==(other) + other = FloatVal.value(other, usage, position) + other.valid? and other.value == value + end + end + + class Empty < Valid + def value + nil + end + + def empty? + true end # @return [String] + # :nocov: def inspect id = definition.try do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -147,6 +170,7 @@ def inspect ansi.element(" R.empty#{id}") end + # :nocov: # @return [String] def to_s @@ -157,39 +181,20 @@ def to_s def to_x12(truncate = true) "" end - - # @return [Boolean] - def ==(other) - other.is_a?(Empty) or other.nil? - end - - # @return [Empty] - def copy(changes = {}) - FloatVal.value \ - changes.fetch(:value, nil), - changes.fetch(:usage, @usage), - changes.fetch(:position, @position) - end end - # - # Non-empty numeric value. Shouldn't be directly instantiated -- - # instead, use the {FloatVal.value} constructors. - # - class NonEmpty < FloatVal - include Comparable - + class NonEmpty < Valid # @group Mathematical Operators ################################################################# extend Operators::Binary - binary_operators(:+, :-, :*, :/, :%, :coerce => :to_d) - - extend Operators::Relational - relational_operators(:==, :<=>, :coerce => :to_d) + binary_operators :+, :-, :*, :/, :%, :coerce => :to_d extend Operators::Unary - unary_operators(:abs, :-@, :+@) + unary_operators :abs, :-@, :+@ + + extend Operators::Relational + relational_operators :<, :>, :<=, :>=, :<=>, :coerce => :to_d # @endgroup ################################################################# @@ -197,35 +202,19 @@ class NonEmpty < FloatVal # @return [BigDecimal] attr_reader :value - def_delegators :@value, :to_i, :to_d, :to_f, :to_r, :to_c + def_delegators :value, :to_i, :to_d, :to_f, :to_r, :to_c def initialize(value, usage, position) @value = value super(usage, position) end - # @return [NonEmpty] - def copy(changes = {}) - NonEmpty.new \ - changes.fetch(:value, @value), - changes.fetch(:usage, usage), - changes.fetch(:position, position) - end - - def coerce(other) - return copy(:value => other.to_d), self - end - - def valid? - # False for NaN and +/- Infinity - @value.finite? - end - def empty? false end # @return [String] + # :nocov: def inspect id = definition.try do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -241,6 +230,7 @@ def inspect ansi.element(" R.value#{id}") + "(#{to_s})" end + # :nocov: # @return [String] def to_s @@ -267,15 +257,12 @@ def to_x12(truncate = true) definition.max_length - @value.to_i.abs.to_s.length end - if remaining <= 0 - if truncate - int = @value.to_i - sign = (int < 0) ? "-" : "" - sign = sign + int.abs.to_s.take(definition.max_length) - return sign - else - return @value.to_i.abs - end + # The integer part consumes all the space, so there is no room + # for a fractional amount + if remaining <= 0 and truncate + int = @value.to_i + sign = (int < 0) ? "-" : "" + return sign + int.abs.to_s.take(definition.max_length) end # Don't exceed the definition's max_precision @@ -313,11 +300,6 @@ def too_long? # The length of a decimal type does not include an optional sign definition.max_length < @value.to_i.abs.to_s.length end - - # @return [FloatVal] - def map - FloatVal.value(yield(@value), usage, position) - end end end @@ -332,7 +314,9 @@ def empty(usage, position) # @return [FloatVal] def value(object, usage, position) - if object.blank? + if object.is_a?(FloatVal) + object#.copy(:usage => usage, :position => position) + elsif object.blank? self::Empty.new(usage, position) else self::NonEmpty.new(object.to_d, usage, position) diff --git a/lib/stupidedi/versions/common/element_types/tm.rb b/lib/stupidedi/versions/common/element_types/tm.rb index 06a9cc7ba..318639490 100644 --- a/lib/stupidedi/versions/common/element_types/tm.rb +++ b/lib/stupidedi/versions/common/element_types/tm.rb @@ -60,10 +60,11 @@ def empty? # @return [TimeVal] def map - TimeVal.value(yield(nil, nil, nil), usage, position) + self end # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -79,6 +80,7 @@ def inspect ansi.element("TM.invalid#{id}") + "(#{ansi.invalid(@value.inspect)})" end + # :nocov: # @return [String] def to_s @@ -100,20 +102,45 @@ def copy(changes = {}) end end - # - # Empty time value. Shouldn't be directly instantiated -- instead, - # use the {TimeVal.empty} constructor. - # - class Empty < TimeVal + class Valid < TimeVal def valid? true end + # @return [TimeVal] + def map + TimeVal.value(yield(value), usage, position) + end + + def copy(changes = {}) + TimeVal.value \ + changes.fetch(:value, value), + changes.fetch(:usage, usage), + changes.fetch(:position, position) + end + + def coerce(other) + return TimeVal.value(other, usage, position), self + end + + # @return [Boolean] + def ==(other) + other = TimeVal.value(other, usage, position) + other.valid? and other.value == value + end + end + + class Empty < Valid + def value + nil + end + def empty? true end # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -129,11 +156,7 @@ def inspect ansi.element("TM.empty#{id}") end - - # @return [TimeVal] - def map - TimeVal.value(yield(nil, nil, nil), usage, position) - end + # :nocov: # @return [String] def to_s @@ -144,24 +167,9 @@ def to_s def to_x12(truncate = true) "" end - - # @return [Boolean] - def ==(other) - other.is_a?(Empty) or other.nil? - end - - def copy(changes = {}) - self - end end - # - # Non-empty time value. Unlike common models of time, this - # encapsulates only an hour, minute, and second (with fractional - # seconds) but not a date. Shouldn't be directly instantiated -- - # instead, use the {TimeVal.value} constructor. - # - class NonEmpty < TimeVal + class NonEmpty < Valid # @return [Integer] attr_reader :hour @@ -188,27 +196,26 @@ def initialize(hour, minute, second, usage, position) end end - # @return [NonEmpty] def copy(changes = {}) - NonEmpty.new \ - changes.fetch(:hour, @hour), - changes.fetch(:minute, @minute), - changes.fetch(:second, @second), - changes.fetch(:usage, usage), - changes.fetch(:position, position) + if [:hour, :minute, :second].any?{|x| changes.include?(x) } + changes[:value] = [changes.fetch(:hour, @hour), + changes.fetch(:minute, @minute), + changes.fetch(:second, @second)] + end + + super(changes) end - def valid? - true + def value + [@hour, @minute, @second] end def empty? false end - # @return [TimeVal] - def map - TimeVal.value(yield(@hour, @minute, @second), usage, position) + def too_short? + to_x12(false).length < definition.min_length end # @return [Time] @@ -226,6 +233,7 @@ def to_time(date, minute = nil, second = nil) end # @return [String] + # :nocov: def inspect id = definition.bind do |d| "[#{"% 5s" % d.id}: #{d.name}]".bind do |s| @@ -242,17 +250,18 @@ def inspect hh = @hour.try{|h| "%02d" % h } || "hh" mm = @minute.try{|m| "%02d" % m } || "mm" ss = @second.try{|s| s.to_s("F") } || "ss" - ss = ss.gsub(/^0*\.|0+$/, "") + ss = ss.gsub(/^0*\.|\.0*$/, "") ansi.element("TM.value#{id}") + "(#{hh}:#{mm}:#{ss})" end + # :nocov: # @return [String] def to_s(hh = "hh", mm = "mm", ss = "ss") hh = @hour.try{|h| "%02d" % h } || hh mm = @minute.try{|m| "%02d" % m } || mm ss = @second.try{|s| "%02f" % s } || ss - "#{hh}#{mm}#{ss}" + "#{hh}:#{mm}:#{ss}".gsub(/\.?0*$|:$/, "") end # @return [String] @@ -266,19 +275,6 @@ def to_x12(truncate = true) truncate ? x12.take(definition.max_length) : x12 end - - def too_short? - to_x12(false).length < definition.min_length - end - - # @return [Boolean] - def ==(other) - eql?(other) or - (other.is_a?(TimeVal::NonEmpty) and - other.hour == @hour and - other.minute == @minute and - other.second == @second) - end end end @@ -293,7 +289,9 @@ def empty(usage, position) # @return [TimeVal] def value(object, usage, position) - if object.blank? + if object.is_a?(TimeVal) + object#.copy(:usage => usage, :position => position) + elsif object.blank? self::Empty.new(usage, position) elsif object.is_a?(String) or object.is_a?(StringVal) return self::Invalid.new(object, usage, position) \ @@ -325,6 +323,8 @@ def value(object, usage, position) object.respond_to?(:minute) and object.respond_to?(:second) self::NonEmpty.new(object.hour, object.minute, object.second.to_d, usage, position) + elsif object.is_a?(Array) and object.length == 3 + self::NonEmpty.new(*object, usage, position) else self::Invalid.new(object, usage, position) end diff --git a/spec/lib/stupidedi/transaction_sets/005010/X221-HP835_spec.rb b/spec/lib/stupidedi/transaction_sets/005010/X221-HP835_spec.rb index 48f1e77e9..bca00afe9 100644 --- a/spec/lib/stupidedi/transaction_sets/005010/X221-HP835_spec.rb +++ b/spec/lib/stupidedi/transaction_sets/005010/X221-HP835_spec.rb @@ -1,5 +1,6 @@ describe "Stupidedi::TransactionSets::FiftyTen::Implementations::X221A1::HP835" do using Stupidedi::Refinements + include TreeMatchers include NavigationMatchers let(:fixdir) { "005010/X221 HP835 Health Care Claim Payment Advice/case" } @@ -130,7 +131,7 @@ # 2000 HEADER NUMBER S(:LX, "130212") => Ss(R(:LX, "110212"), - S(:TS3, "6543210909", "13", "1996/12/31", 1, 15000), + S(:TS3, "6543210909", "13", "19961231", 1, 15000), S(:CLP, "777777") => Ss(S(:CAS, "CO", "45", "3019.67"), S(:NM1, "QC"), diff --git a/spec/lib/stupidedi/versions/common/element_types/an_spec.rb b/spec/lib/stupidedi/versions/common/element_types/an_spec.rb index 1358887bd..4653d67f4 100644 --- a/spec/lib/stupidedi/versions/common/element_types/an_spec.rb +++ b/spec/lib/stupidedi/versions/common/element_types/an_spec.rb @@ -1,4 +1,4 @@ -describe Stupidedi::Versions::Common::ElementTypes::StringVal do +describe Stupidedi::Versions::Common::ElementTypes::AN do using Stupidedi::Refinements include Definitions include TimeStringMatchers @@ -12,6 +12,16 @@ let(:position) { Stupidedi::Reader::Position.new(100, 4, 19, "test.x12") } + let(:invalid_str) do + "i'm angry!!".tap do |invalid| + class << invalid + def to_s + raise "grr" + end + end + end + end + def value(x) element_use.value(x, position) end @@ -27,237 +37,31 @@ def value(x) end context "::Invalid" do - let(:invalid) do - "i'm angry!!".tap do |invalid| - class << invalid - def to_s - raise "grr" - end - end - end - end + let(:invalid_val) { value(invalid_str) } + include_examples "global_element_types_invalid" - let(:invalid_val) do - value(invalid) - end - - describe "#id?" do + describe "#string?" do specify { expect(invalid_val).to be_string } end - - describe "#position" do - specify { expect(invalid_val.position).to eql(position) } - end - - describe "#valid?" do - specify { expect(invalid_val).to_not be_valid } - end - - describe "#invalid?" do - specify { expect(invalid_val).to be_invalid } - end - - describe "#empty?" do - specify { expect(invalid_val).to_not be_empty } - end - - describe "#too_short?" do - specify { expect(invalid_val).to_not be_too_short } - end - - describe "#too_short?" do - specify { expect(invalid_val).to_not be_too_long } - end - - describe "#map(&block)" do - specify { expect{|b| invalid_val.map(&b) }.to yield_with_args(nil) } - specify { expect(invalid_val.map { "xx" }).to eq(value("xx")) } - specify { expect(invalid_val.map { nil }).to eq(value("")) } - end - - describe "#to_s" do - specify { expect(invalid_val.to_s).to eq("") } - end - - describe "#to_x12" do - context "with truncation" do - specify { expect(invalid_val.to_x12(true)).to eql("") } - end - - context "without truncation" do - specify { expect(invalid_val.to_x12(false)).to eql("") } - end - end - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(invalid_val.inspect).to be_a(String) - end - - it "indicates invalid" do - expect(invalid_val.inspect).to match(/invalid/) - end - end - - context "when forbidden" do - let(:invalid_val) do - element_use.copy(:requirement => e_not_used).value(invalid, position) - end - - include_examples "inspect" - end - - context "when required" do - let(:invalid_val) do - element_use.copy(:requirement => e_mandatory).value(invalid, position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:invalid_val) do - element_use.copy(:requirement => e_optional).value(invalid, position) - end - - include_examples "inspect" - end - end - - describe "#==(other)" do - specify { expect(invalid_val).to eq(invalid_val) } - specify { expect(invalid_val).to_not eq(value("")) } - specify { expect(invalid_val).to_not eq("") } - end - - describe "#copy(changes)" do - specify { expect(invalid_val.copy).to eql(invalid_val) } - end end context "::Empty" do let(:empty_val) { value("") } + let(:valid_str) { "TEA TIME" } + include_examples "global_element_types_empty" describe "#string?" do specify { expect(empty_val).to be_string } end - - describe "#position" do - specify { expect(empty_val.position).to eql(position) } - end - - describe "#empty?" do - specify { expect(empty_val).to be_empty } - end - - describe "#valid?" do - specify { expect(empty_val).to be_valid } - end - - describe "#invalid?" do - specify { expect(empty_val).not_to be_invalid } - end - - describe "#too_short?" do - specify { expect(empty_val).to_not be_too_short } - end - - describe "#too_short?" do - specify { expect(empty_val).to_not be_too_long } - end - - describe "#map(&block)" do - specify { expect{|b| empty_val.map(&b) }.to yield_with_args(nil) } - specify { expect(empty_val.map { "value" }).to eq("value") } - specify { expect(empty_val.map { "value" }).to eq("value") } - end - - describe "#to_s" do - specify { expect(empty_val.to_s).to eql("") } - end - - describe "#to_x12(truncate)" do - context "with truncation" do - specify { expect(empty_val.to_x12(true)).to eql("") } - end - - context "without truncation" do - specify { expect(empty_val.to_x12(false)).to eql("") } - end - end - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(empty_val.inspect).to be_a(String) - end - - it "indicates empty" do - expect(empty_val.inspect).to match(/empty/) - end - end - - context "when forbidden" do - let(:empty_val) do - element_use.copy(:requirement => e_not_used).empty(position) - end - - include_examples "inspect" - end - - context "when required" do - let(:empty_val) do - element_use.copy(:requirement => e_mandatory).empty(position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:empty_val) do - element_use.copy(:requirement => e_optional).empty(position) - end - - include_examples "inspect" - end - end - - describe "#==(other)" do - specify { expect(empty_val).to eq("") } - specify { expect(empty_val).to eq(empty_val) } - specify { expect(empty_val).to eq(element_use.value("", nil)) } - end - - describe "#coerce(other)" do - specify { expect("" + empty_val).to eq("") } - specify { expect(empty_val + "").to eq("") } - end - - describe "#copy(changes)" do - specify { expect(empty_val.copy).to eql(empty_val) } - end end context "::NonEmpty" do - describe "#string?" do - specify { expect(value("ABC")).to be_string } - end - - describe "#position" do - specify { expect(value("ABC").position).to eql(position) } - end + let(:element_val) { value("ABC XYZ") } + let(:valid_str) { "TEA TIME" } + include_examples "global_element_types_non_empty" - describe "#empty?" do - specify { expect(value("ABC")).to_not be_empty } - end - - describe "#valid?" do - specify { expect(value("ABC")).to be_valid } - end - - describe "#invalid?" do - specify { expect(value("ABC")).not_to be_invalid } + describe "#string?" do + specify { expect(element_val).to be_string } end describe "#too_short?" do @@ -304,46 +108,6 @@ def to_s end end - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(element_val.inspect).to be_a(String) - end - - it "indicates valid" do - expect(element_val.inspect).to match(/value/) - end - - it "indicates value" do - expect(element_val.inspect).to match(/#{element_val.value}/) - end - end - - context "when forbidden" do - let(:element_val) do - element_use.copy(:requirement => e_not_used).value("abc", position) - end - - include_examples "inspect" - end - - context "when required" do - let(:element_val) do - element_use.copy(:requirement => e_mandatory).value("abc", position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:element_val) do - element_use.copy(:requirement => e_optional).value("abc", position) - end - - include_examples "inspect" - end - end - describe "#length" do specify { expect(value("abc").length).to eq(3) } specify { expect(value("abc ").length).to eq(3) } @@ -398,10 +162,10 @@ def to_s end describe "#==(other)" do - specify { expect(value("ABC")).not_to eq("") } - specify { expect(value("ABC")).not_to eq("ABC ") } - specify { expect(value("ABC")).not_to eq(" ABC") } - specify { expect(value("ABC")).not_to eq(" ABC ") } + specify { expect(value("ABC")).to_not eq("") } + specify { expect(value("ABC")).to eq("ABC ") } + specify { expect(value("ABC")).to_not eq(" ABC") } + specify { expect(value("ABC")).to_not eq(" ABC ") } specify { expect(value("ABC")).to eq("ABC") } specify { expect(value("ABC")).to eq(value("ABC")) } specify { expect(value("ABC")).to eq(element_use.value("ABC", nil)) } @@ -414,10 +178,5 @@ def to_s specify { expect("ABC" + value("abc")).to eq("ABCabc") } specify { expect(value("ABC") + "abc").to eq("ABCabc") } end - - describe "#copy(changes)" do - specify { expect(value("abc").copy(:value => "")).to be_empty } - specify { expect(value("abc").copy(:value => "").position).to eql(position) } - end end end diff --git a/spec/lib/stupidedi/versions/common/element_types/dt_spec.rb b/spec/lib/stupidedi/versions/common/element_types/dt_spec.rb index 34f96608c..3f6e5e793 100644 --- a/spec/lib/stupidedi/versions/common/element_types/dt_spec.rb +++ b/spec/lib/stupidedi/versions/common/element_types/dt_spec.rb @@ -65,6 +65,8 @@ def value_6(x) context "::Invalid" do let(:invalid_val) { value_8("20090230") } + let(:element_use) { element_use_8 } + include_examples "global_element_types_invalid" describe "#date?" do specify { expect(invalid_val).to be_date } @@ -73,103 +75,14 @@ def value_6(x) describe "#proper?" do specify { expect(invalid_val).to_not be_proper } end - - describe "#position" do - specify { expect(invalid_val.position).to eql(position) } - end - - describe "#empty?" do - specify { expect(invalid_val).to_not be_empty } - end - - describe "#valid?" do - specify { expect(invalid_val).to_not be_valid } - end - - describe "#invalid?" do - specify { expect(invalid_val).to be_invalid } - end - - describe "#too_short?" do - specify { expect(invalid_val).to_not be_too_short } - end - - describe "#too_long?" do - specify { expect(invalid_val).to_not be_too_long } - end - - describe "#map(&block)" do - specify { expect{|b| invalid_val.map(&b) }.to yield_with_args(nil) } - specify { expect(invalid_val.map { "70130" }).to be_invalid } - specify { expect(invalid_val.map {|x| x }).to eq(value_8("")) } - specify { expect(invalid_val.map { "20200130" }).to eq(Date.civil(2020, 1, 30)) } - end - - todo "#==(other)" - - describe "#to_s" do - it "is an empty string" do - expect(invalid_val.to_s).to eq("") - end - end - - describe "#to_x12" do - context "with truncation" do - it "is an empty string" do - expect(invalid_val.to_x12(true)).to eq("") - end - end - - context "without truncation" do - it "is an empty string" do - expect(invalid_val.to_x12(false)).to eq("") - end - end - end - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(invalid_val.inspect).to be_a(String) - end - - it "indicates invalid" do - expect(invalid_val.inspect).to match(/invalid/) - end - - it "includes element value" do - expect(invalid_val.inspect).to match(/#{invalid_val.value.inspect}/) - end - end - - context "when forbidden" do - let(:invalid_val) do - element_use_8.copy(:requirement => e_not_used).value("boo", position) - end - - include_examples "inspect" - end - - context "when required" do - let(:invalid_val) do - element_use_8.copy(:requirement => e_mandatory).value("boo", position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:invalid_val) do - element_use_8.copy(:requirement => e_optional).value("boo", position) - end - - include_examples "inspect" - end - end end context "::Empty" do - let(:empty_val) { value_8("") } + let(:empty_val) { value_8("") } + let(:valid_str) { Date.civil(2020, 12, 31) } + let(:invalid_str) { "20201300" } + let(:element_use) { element_use_8 } + include_examples "global_element_types_empty" describe "#date?" do specify { expect(empty_val).to be_date } @@ -178,115 +91,18 @@ def value_6(x) describe "#proper?" do specify { expect(empty_val).to_not be_proper } end - - describe "#position" do - specify { expect(empty_val.position).to eql(position) } - end - - describe "#empty?" do - specify { expect(empty_val).to be_empty } - end - - describe "#valid?" do - specify { expect(empty_val).to be_valid } - end - - describe "#invalid?" do - specify { expect(empty_val).not_to be_invalid } - end - - describe "#too_short?" do - specify { expect(empty_val).to_not be_too_short } - end - - describe "#too_long?" do - specify { expect(empty_val).to_not be_too_long } - end - - describe "#map(&block)" do - specify { expect{|b| empty_val.map(&b) }.to yield_with_args(nil) } - specify { expect(empty_val.map { "70130" }).to be_invalid } - specify { expect(empty_val.map { "20110713" }).to eq(Date.civil(2011, 7, 13)) } - specify { expect(empty_val.map {|x| x }).to eq(empty_val) } - end - - describe "#to_s" do - specify { expect(empty_val.to_s).to eql("") } - end - - describe "#to_x12(truncate)" do - context "with truncation" do - specify { expect(empty_val.to_x12(true)).to eql("") } - end - - context "without truncation" do - specify { expect(empty_val.to_x12(false)).to eql("") } - end - end - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(empty_val.inspect).to be_a(String) - end - - it "indicates empty " do - expect(empty_val.inspect).to match(/empty/) - end - end - - context "when forbidden" do - let(:empty_val) do - element_use_8.copy(:requirement => e_not_used).value("", position) - end - - include_examples "inspect" - end - - context "when required" do - let(:empty_val) do - element_use_8.copy(:requirement => e_mandatory).value("", position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:empty_val) do - element_use_8.copy(:requirement => e_optional).value("", position) - end - - include_examples "inspect" - end - end - - describe "#==(other)" do - specify { expect(empty_val).to eq(nil) } - specify { expect(empty_val).to eq(empty_val) } - specify { expect(empty_val).to eq(value_8("")) } - specify { expect(empty_val).not_to eq(value_8(Date.today)) } - end end context "::Proper" do - describe "#position" do - specify { expect(value_8(Date.today).position).to eql(position) } - end + let(:element_val) { value_8("20180630") } + let(:element_use) { element_use_8 } + let(:valid_str) { Date.civil(2020, 12, 31) } + let(:invalid_str) { "wrong" } + let(:inspect_str) { "2018-06-30" } + include_examples "global_element_types_non_empty" describe "#proper?" do - specify { expect(value_8(Date.today)).to be_proper } - end - - describe "#empty?" do - specify { expect(value_8(Date.today)).to_not be_empty } - end - - describe "#valid?" do - specify { expect(value_8(Date.today)).to be_valid } - end - - describe "#invalid?" do - specify { expect(value_8(Date.today)).not_to be_invalid } + specify { expect(element_val).to be_proper } end describe "#too_short?" do @@ -498,24 +314,19 @@ def value_6(x) end context "::Improper" do - let(:improper_val) { value_8("200411") } + let(:element_val) { value_6("200311") } + let(:element_use) { element_use_6 } + let(:invalid_str) { "wrong" } + let(:valid_str) { "201231" } + let(:inspect_str) { "XX20-03-11" } + include_examples "global_element_types_non_empty" - describe "#valid?" do - specify { expect(improper_val).to be_valid } - end - - describe "#invalid?" do - specify { expect(improper_val).to_not be_invalid } - end + let(:improper_val) { value_6("200411") } describe "#proper?" do specify { expect(improper_val).to_not be_proper } end - describe "#empty?" do - specify { expect(improper_val).to_not be_empty } - end - describe "#copy(changes)" do specify { expect(improper_val.copy).to eq(improper_val) } specify { expect(improper_val.copy(:year => 21)).to eq(value_8("210411")) } @@ -565,46 +376,6 @@ def value_6(x) end end - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(improper_val.inspect).to be_a(String) - end - - it "indicates valid" do - expect(improper_val.inspect).to match(/value/) - end - - it "includes element value" do - expect(improper_val.inspect).to match(/\d{2}-\d{2}-\d{2}/) - end - end - - context "when forbidden" do - let(:improper_val) do - element_use_8.copy(:requirement => e_not_used).value("190131", position) - end - - include_examples "inspect" - end - - context "when required" do - let(:improper_val) do - element_use_8.copy(:requirement => e_mandatory).value("190131", position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:improper_val) do - element_use_8.copy(:requirement => e_optional).value("190131", position) - end - - include_examples "inspect" - end - end - describe "#to_s" do specify { expect(improper_val.to_s).to eq("XX200411") } end diff --git a/spec/lib/stupidedi/versions/common/element_types/id_spec.rb b/spec/lib/stupidedi/versions/common/element_types/id_spec.rb index 9ec95a0b7..3dc22c30e 100644 --- a/spec/lib/stupidedi/versions/common/element_types/id_spec.rb +++ b/spec/lib/stupidedi/versions/common/element_types/id_spec.rb @@ -11,239 +11,46 @@ let(:position) { Stupidedi::Reader::Position.new(100, 4, 19, "test.x12") } + let(:invalid_str) do + "i'm angry!!".tap do |invalid| + class << invalid + def to_s + raise "grr" + end + end + end + end + def value(x) element_use.value(x, position) end context "::Invalid" do - let(:invalid) do - "i'm angry!!".tap do |invalid| - class << invalid - def to_s - raise "grr" - end - end - end - end - - let(:invalid_val) do - value(invalid) - end + let(:invalid_val) { value(invalid_str) } + include_examples "global_element_types_invalid" describe "#id?" do specify { expect(invalid_val).to be_id } end - - describe "#position" do - specify { expect(invalid_val.position).to eql(position) } - end - - describe "#valid?" do - specify { expect(invalid_val).to_not be_valid } - end - - describe "#invalid?" do - specify { expect(invalid_val).to be_invalid } - end - - describe "#empty?" do - specify { expect(invalid_val).to_not be_empty } - end - - describe "#too_short?" do - specify { expect(invalid_val).to_not be_too_short } - end - - describe "#too_short?" do - specify { expect(invalid_val).to_not be_too_long } - end - - describe "#map(&block)" do - specify { expect{|b| invalid_val.map(&b) }.to yield_with_args(nil) } - specify { expect(invalid_val.map { "xx" }).to eq(value("xx")) } - specify { expect(invalid_val.map { nil }).to eq(value("")) } - end - - describe "#to_s" do - specify { expect(invalid_val.to_s).to eq("") } - end - - describe "#to_x12" do - context "with truncation" do - specify { expect(invalid_val.to_x12(true)).to eql("") } - end - - context "without truncation" do - specify { expect(invalid_val.to_x12(false)).to eql("") } - end - end - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(invalid_val.inspect).to be_a(String) - end - - it "indicates invalid" do - expect(invalid_val.inspect).to match(/invalid/) - end - end - - context "when forbidden" do - let(:invalid_val) do - element_use.copy(:requirement => e_not_used).value(invalid, position) - end - - include_examples "inspect" - end - - context "when required" do - let(:invalid_val) do - element_use.copy(:requirement => e_mandatory).value(invalid, position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:invalid_val) do - element_use.copy(:requirement => e_optional).value(invalid, position) - end - - include_examples "inspect" - end - end - - describe "#==(other)" do - specify { expect(invalid_val).to eq(invalid_val) } - specify { expect(invalid_val).to_not eq(value("")) } - specify { expect(invalid_val).to_not eq("") } - end - - describe "#copy(changes)" do - specify { expect(invalid_val.copy).to eql(invalid_val) } - end end context "::Empty" do let(:empty_val) { value("") } + let(:valid_str) { "X" } + include_examples "global_element_types_empty" describe "#id?" do specify { expect(empty_val).to be_id } end - - describe "#position" do - specify { expect(empty_val.position).to eql(position) } - end - - describe "#empty?" do - specify { expect(empty_val).to be_empty } - end - - describe "#valid?" do - specify { expect(empty_val).to be_valid } - end - - describe "#invalid?" do - specify { expect(empty_val).not_to be_invalid } - end - - describe "#too_short?" do - specify { expect(empty_val).to_not be_too_short } - end - - describe "#too_short?" do - specify { expect(empty_val).to_not be_too_long } - end - - describe "#map(&block)" do - specify { expect{|b| empty_val.map(&b) }.to yield_with_args(nil) } - specify { expect(empty_val.map { "x" }).to eq(value("x")) } - specify { expect(empty_val.map { nil }).to eq(value("")) } - end - - describe "#to_s" do - specify { expect(empty_val.to_s).to eq("") } - end - - describe "#to_x12(truncate)" do - context "with truncation" do - specify { expect(empty_val.to_x12(true)).to eql("") } - end - - context "without truncation" do - specify { expect(empty_val.to_x12(false)).to eql("") } - end - end - - describe "#==(other)" do - specify { expect(empty_val).to eq(empty_val) } - specify { expect(empty_val).to eq("") } - specify { expect(empty_val).to eq(value("")) } - end - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(empty_val.inspect).to be_a(String) - end - - it "indicates empty" do - expect(empty_val.inspect).to match(/empty/) - end - end - - context "when forbidden" do - let(:empty_val) do - element_use.copy(:requirement => e_not_used).empty(position) - end - - include_examples "inspect" - end - - context "when required" do - let(:empty_val) do - element_use.copy(:requirement => e_mandatory).empty(position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:empty_val) do - element_use.copy(:requirement => e_optional).empty(position) - end - - include_examples "inspect" - end - end - - describe "#copy(changes)" do - specify { expect(empty_val.copy).to eq(empty_val) } - specify { expect(empty_val.copy(:value => "abc")).to eq(value("abc")) } - specify { expect(empty_val.copy(:value => "abc").position).to eql(position) } - end end context "::NonEmpty" do - describe "#position" do - specify { expect(value("ABC").position).to eql(position) } - end + let(:element_val) { value("ABC") } + let(:valid_str) { "XYZ" } + include_examples "global_element_types_non_empty" describe "#id?" do - specify { expect(value("ABC")).to be_id } - end - - describe "#empty?" do - specify { expect(value("ABC")).to_not be_empty } - end - - describe "#valid?" do - specify { expect(value("ABC")).to be_valid } - end - - describe "#invalid?" do - specify { expect(value("ABC")).to_not be_invalid } + specify { expect(element_val).to be_id } end describe "#too_short?" do @@ -289,11 +96,11 @@ def to_s specify { expect(value("abc").map(&:upcase)).to be_id } end - describe "#==(Other)" do - specify { expect(value("ABC")).not_to eq("") } - specify { expect(value("ABC")).not_to eq("ABC ") } - specify { expect(value("ABC")).not_to eq(" ABC") } - specify { expect(value("ABC")).not_to eq(" ABC ") } + describe "#==(other)" do + specify { expect(value("ABC")).to_not eq("") } + specify { expect(value("ABC")).to eq("ABC ") } + specify { expect(value("ABC")).to_not eq(" ABC") } + specify { expect(value("ABC")).to_not eq(" ABC ") } specify { expect(value("ABC")).to eq("ABC") } specify { expect(value("ABC")).to eq(value("ABC")) } specify { expect(value("ABC")).to eq(element_use.value("ABC", nil)) } @@ -301,78 +108,5 @@ def to_s specify { expect(value(" ABC")).to eq(" ABC") } specify { expect(value(" ABC ")).to eq(" ABC") } end - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(element_val.inspect).to be_a(String) - end - - it "indicates valid" do - expect(element_val.inspect).to match(/value/) - end - - it "indicates value" do - expect(element_val.inspect).to match(/#{element_val.value}/) - end - end - - context "when forbidden" do - let(:element_val) do - element_use.copy(:requirement => e_not_used).value("abc", position) - end - - include_examples "inspect" - end - - context "when required" do - let(:element_val) do - element_use.copy(:requirement => e_mandatory).value("abc", position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:element_val) do - element_use.copy(:requirement => e_optional).value("abc", position) - end - - include_examples "inspect" - end - - context "with an internal code list" do - let(:element_use_) do - element_use.copy( - :allowed_values => Stupidedi::Sets.absolute(["A"]), - :definition => element_use.definition.copy( - :code_list => Stupidedi::Schema::CodeList.build("A" => "OK", "B" => "NO"))) - end - - context "and an allowed value" do - it "indicates code meaning" do - expect(element_use_.value("A", position).inspect).to match(/OK/) - end - end - - context "and an unallowed value" do - it "indicates code meaning" do - expect(element_use_.value("B", position).inspect).to match(/NO/) - end - end - - context "and an unknown value" do - it "does not indicate code meaning" do - expect(element_use_.value("C", position).inspect).to_not match(/OK|NO/) - end - end - end - end - - describe "#copy(changes" do - specify { expect(value("abc").copy).to eq(value("abc")) } - specify { expect(value("abc").copy(:value => "")).to eq(value("")) } - specify { expect(value("abc").copy(:value => "").position).to eql(position) } - end end end diff --git a/spec/lib/stupidedi/versions/common/element_types/nn_spec.rb b/spec/lib/stupidedi/versions/common/element_types/nn_spec.rb index 50f7e5419..2d411b7f6 100644 --- a/spec/lib/stupidedi/versions/common/element_types/nn_spec.rb +++ b/spec/lib/stupidedi/versions/common/element_types/nn_spec.rb @@ -24,233 +24,68 @@ def value(x) end describe "::Invalid" do - let(:invalid_val_a) { value("1A") } - let(:invalid_val_b) { value("A1") } + let(:invalid_val) { value("1A") } + include_examples "global_element_types_invalid" describe "#numeric?" do - specify { expect(invalid_val_a).to be_numeric } - specify { expect(invalid_val_b).to be_numeric } + specify { expect(invalid_val).to be_numeric } end - describe "#position" do - specify { expect(invalid_val_a.position).to eql(position) } - specify { expect(invalid_val_b.position).to eql(position) } + describe "#to_d" do + specify { expect(invalid_val).to_not respond_to(:to_d) } end - describe "#empty?" do - specify { expect(invalid_val_a).not_to be_empty } - specify { expect(invalid_val_b).not_to be_empty } + describe "#to_f" do + specify { expect(invalid_val).to_not respond_to(:to_f) } end - describe "#valid?" do - specify { expect(invalid_val_a).not_to be_valid } - specify { expect(invalid_val_b).not_to be_valid } + describe "#to_i" do + specify { expect(invalid_val).to_not respond_to(:to_i) } end - describe "#invalid?" do - specify { expect(invalid_val_a).to be_invalid } - specify { expect(invalid_val_b).to be_invalid } - end - - todo "#too_long?" - todo "#too_short?" - - describe "#map(&block)" do - specify { expect{|b| invalid_val_a.map(&b) }.to yield_with_args(nil) } - specify { expect(invalid_val_a.map{|x| "1"}).to eq(value("1")) } - end - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(invalid_val.inspect).to be_a(String) - end - - it "indicates invalid" do - expect(invalid_val.inspect).to match(/invalid/) - end - end - - context "when forbidden" do - let(:invalid_val) do - element_use.copy(:requirement => e_not_used).value("A1", position) - end - - include_examples "inspect" - end - - context "when required" do - let(:invalid_val) do - element_use.copy(:requirement => e_mandatory).value("A1", position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:invalid_val) do - element_use.copy(:requirement => e_optional).value("A1", position) - end - - include_examples "inspect" - end - end - - describe "#to_s" do - specify { expect(invalid_val_a.to_s).to eq("") } - end - - todo "#to_d" - todo "#to_f" - todo "#to_i" - todo "#to_r" - - context "#to_x12(truncate)" do - context "with truncation" do - specify { expect(invalid_val_a.to_x12(true)).to eq("") } - end - - context "without truncation" do - specify { expect(invalid_val_a.to_x12(false)).to eq("") } - end - end - - describe "#==(other)" do - specify { expect(invalid_val_a).to eq(invalid_val_a) } - specify { expect(invalid_val_a).to_not eq(invalid_val_b) } - end - - describe "#copy(changes)" do - specify { expect(invalid_val_a.copy).to eql(invalid_val_a) } + describe "#to_r" do + specify { expect(invalid_val).to_not respond_to(:to_r) } end end describe "::Empty" do - let(:empty_val) { value("") } + let(:empty_val) { value("") } + let(:valid_str) { "900" } + let(:invalid_str) { "::" } + include_examples "global_element_types_empty" describe "#numeric?" do specify { expect(empty_val).to be_numeric } end - describe "#position" do - specify { expect(empty_val.position).to eql(position) } - end - - describe "#empty?" do - specify { expect(empty_val).to be_empty } - end - - describe "#valid?" do - specify { expect(empty_val).to be_valid } - end - - describe "#invalid?" do - specify { expect(empty_val).to_not be_invalid } - end - - describe "#too_short?" do - specify { expect(empty_val).to_not be_too_short } - end - - describe "#too_short?" do - specify { expect(empty_val).to_not be_too_long } - end - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(empty_val.inspect).to be_a(String) - end - - it "indicates empty" do - expect(empty_val.inspect).to match(/empty/) - end - end - - context "when forbidden" do - let(:empty_val) do - element_use.copy(:requirement => e_not_used).empty(position) - end - - include_examples "inspect" - end - - context "when required" do - let(:empty_val) do - element_use.copy(:requirement => e_mandatory).empty(position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:empty_val) do - element_use.copy(:requirement => e_optional).empty(position) - end - - include_examples "inspect" - end - end - - describe "#map(&block)" do - specify { expect{|b| empty_val.map(&b) }.to yield_with_args(nil) } - specify { expect(empty_val.map { "123" }).to eq("1.23") } - specify { expect(empty_val.map { "123" }).to be_numeric } + describe "#to_d" do + specify { expect(empty_val).to_not respond_to(:to_d) } end - describe "#to_s" do - specify { expect(empty_val.to_s).to eql("") } - end - - todo "#to_d" - todo "#to_f" - todo "#to_i" - todo "#to_r" - - describe "#to_x12(truncate)" do - context "with truncation" do - specify { expect(empty_val.to_x12(true)).to eql("") } - end - - context "without truncation" do - specify { expect(empty_val.to_x12(false)).to eql("") } - end + describe "#to_f" do + specify { expect(empty_val).to_not respond_to(:to_f) } end - describe "#==(other)" do - specify { expect(empty_val).to eq(empty_val) } - specify { expect(empty_val).to eq(nil) } - specify { expect(empty_val).to eq(value("")) } + describe "#to_i" do + specify { expect(empty_val).to_not respond_to(:to_i) } end - describe "#copy(changes)" do - specify { expect(empty_val.copy).to eq(empty_val) } - specify { expect(empty_val.copy(:value => "1234")).to eq("12.34") } - specify { expect(empty_val.copy(:value => "1234").position).to eql(position) } + describe "#to_r" do + specify { expect(empty_val).to_not respond_to(:to_r) } end end context "::NonEmpty" do + let(:element_val) { value("1234") } + let(:inspect_str) { "12.34" } + let(:valid_str) { "987" } + let(:invalid_str) { "wrong" } + include_examples "global_element_types_non_empty" + describe "#numeric?" do specify { expect(value("1234")).to be_numeric } end - describe "#position" do - specify { expect(value("1234").position).to eql(position) } - end - - describe "#valid?" do - specify { expect(value("1234")).to be_valid } - end - - describe "#invalid?" do - specify { expect(value("1234")).to_not be_invalid } - end - - describe "#empty?" do - specify { expect(value("1234")).to_not be_empty } - end - describe "#too_long?" do specify { expect(value("1234")).to_not be_too_long } specify { expect(value("12345678")).to be_too_long } @@ -262,7 +97,7 @@ def value(x) describe "#map(&block)" do specify { expect{|b| value("123").map(&b) }.to yield_with_args("1.23".to_d) } - specify { expect(value("123").map{|x| "100" }).to eq("1.0") } + specify { expect(value("123").map{|x| "100" }).to eq("1.0".to_d) } specify { expect(value("123").map{|x| "A"}).to be_invalid } specify { expect(value("123").map{|x| nil}).to be_empty } end @@ -278,48 +113,6 @@ def value(x) todo "without truncation" end - todo "#==(other)" - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(element_val.inspect).to be_a(String) - end - - it "indicates valid" do - expect(element_val.inspect).to match(/value/) - end - - it "indicates value" do - expect(element_val.inspect).to match(/#{element_val.to_s}/) - end - end - - context "when forbidden" do - let(:element_val) do - element_use.copy(:requirement => e_not_used).value("100", position) - end - - include_examples "inspect" - end - - context "when required" do - let(:element_val) do - element_use.copy(:requirement => e_mandatory).value("100", position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:element_val) do - element_use.copy(:requirement => e_optional).value("100", position) - end - - include_examples "inspect" - end - end - describe "relational operators" do let(:a) { value("1234") } let(:b) { value("1235") } diff --git a/spec/lib/stupidedi/versions/common/element_types/r_spec.rb b/spec/lib/stupidedi/versions/common/element_types/r_spec.rb index 1f7b15a3c..4b032e69f 100644 --- a/spec/lib/stupidedi/versions/common/element_types/r_spec.rb +++ b/spec/lib/stupidedi/versions/common/element_types/r_spec.rb @@ -24,234 +24,62 @@ def value(x) end context "::Invalid" do - let(:invalid_val_a) { value("1A") } - let(:invalid_val_b) { value("A1") } - - describe "#position" do - specify { expect(invalid_val_a.position).to eql(position) } - specify { expect(invalid_val_b.position).to eql(position) } - end + let(:invalid_val) { value("1A") } + include_examples "global_element_types_invalid" describe "#numeric?" do - specify { expect(invalid_val_a).to be_numeric } - specify { expect(invalid_val_b).to be_numeric } - end - - describe "#empty?" do - specify { expect(invalid_val_a).not_to be_empty } - specify { expect(invalid_val_b).not_to be_empty } - end - - describe "#valid?" do - specify { expect(invalid_val_a).not_to be_valid } - specify { expect(invalid_val_b).not_to be_valid } - end - - describe "#invalid?" do - specify { expect(invalid_val_a).to be_invalid } - specify { expect(invalid_val_b).to be_invalid } - end - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(invalid_val.inspect).to be_a(String) - end - - it "indicates invalid" do - expect(invalid_val.inspect).to match(/invalid/) - end - end - - context "when forbidden" do - let(:invalid_val) do - element_use.copy(:requirement => e_not_used).value("A1", position) - end - - include_examples "inspect" - end - - context "when required" do - let(:invalid_val) do - element_use.copy(:requirement => e_mandatory).value("A1", position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:invalid_val) do - element_use.copy(:requirement => e_optional).value("A1", position) - end - - include_examples "inspect" - end - end - - describe "#too_long?" do - specify { expect(invalid_val_a).to_not be_too_long } - end - - describe "#too_short?" do - specify { expect(invalid_val_a).to_not be_too_short } + specify { expect(invalid_val).to be_numeric } end - describe "#map(&block)" do - specify { expect{|b| invalid_val_a.map(&b) }.to yield_with_args(nil) } - specify { expect(invalid_val_a.map{|x| "1"}).to eq(value("1")) } - end - - describe "#to_s" do - specify { expect(invalid_val_a.to_s).to eq("") } + context "#to_d" do + specify { expect(invalid_val).to_not respond_to(:to_d) } end - todo "#to_d" - todo "#to_f" - todo "#to_i" - todo "#to_r" - - context "#to_x12(truncate)" do - context "with truncation" do - specify { expect(invalid_val_a.to_x12(true)).to eq("") } - end - - context "without truncation" do - specify { expect(invalid_val_a.to_x12(false)).to eq("") } - end + context "#to_f" do + specify { expect(invalid_val).to_not respond_to(:to_f) } end - describe "#==(other)" do - specify { expect(invalid_val_a).to eq(invalid_val_a) } - specify { expect(invalid_val_a).to_not eq(invalid_val_b) } + context "#to_i" do + specify { expect(invalid_val).to_not respond_to(:to_i) } end - describe "#copy(changes)" do - specify { expect(invalid_val_a.copy).to eql(invalid_val_a) } + context "#to_r" do + specify { expect(invalid_val).to_not respond_to(:to_r) } end end context "::Empty" do - let(:empty_val) { value("") } - - describe "#position" do - specify { expect(empty_val.position).to eql(position) } - end - - describe "#empty?" do - specify { expect(empty_val).to be_empty } - end - - describe "#valid?" do - specify { expect(empty_val).to be_valid } - end - - describe "#invalid?" do - specify { expect(empty_val).to_not be_invalid } - end - - describe "#too_short?" do - specify { expect(empty_val).not_to be_too_short } - end - - describe "#too_long?" do - specify { expect(empty_val).not_to be_too_long } - end - - describe "#map(&block)" do - specify { expect(empty_val.map { "1.23" }).to eq("1.23") } - specify { expect(empty_val.map { "1.23" }).to be_numeric } - end - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(empty_val.inspect).to be_a(String) - end - - it "indicates empty" do - expect(empty_val.inspect).to match(/empty/) - end - end - - context "when forbidden" do - let(:empty_val) do - element_use.copy(:requirement => e_not_used).empty(position) - end - - include_examples "inspect" - end - - context "when required" do - let(:empty_val) do - element_use.copy(:requirement => e_mandatory).empty(position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:empty_val) do - element_use.copy(:requirement => e_optional).empty(position) - end + let(:empty_val) { value("") } + let(:valid_str) { "1.23" } + let(:invalid_str) { "ABC" } + include_examples "global_element_types_empty" - include_examples "inspect" - end + describe "#numeric?" do + specify { expect(empty_val).to be_numeric } end - describe "#to_s" do - specify { expect(empty_val.to_s).to eq("") } + describe "#to_d" do + specify { expect(empty_val).to_not respond_to(:to_d) } end - todo "#to_d" - todo "#to_f" - todo "#to_i" - todo "#to_r" - - describe "#to_x12(truncate)" do - context "with truncation" do - specify { expect(empty_val.to_x12(true)).to eql("") } - end - - context "without truncation" do - specify { expect(empty_val.to_x12(false)).to eql("") } - end + describe "#to_f" do + specify { expect(empty_val).to_not respond_to(:to_f) } end - describe "#==(other)" do - specify { expect(empty_val).to eq(empty_val) } - specify { expect(empty_val).to eq(nil) } - specify { expect(empty_val).to eq(value("")) } + describe "#to_i" do + specify { expect(empty_val).to_not respond_to(:to_i) } end - describe "#copy(changes)" do - specify { expect(empty_val.copy).to eq(empty_val) } - specify { expect(empty_val.copy(:value => "1.0")).to eq(value("1.0")) } - specify { expect(empty_val.copy(:value => "1.0").position).to eql(position) } + describe "#to_r" do + specify { expect(empty_val).to_not respond_to(:to_r) } end end context "NonEmpty" do - let(:element_val) { value("1.23123") } - - describe "#position" do - specify { expect(element_val.position).to eql(position) } - end - - describe "#empty?" do - specify { expect(element_val).to_not be_empty } - end - - describe "#valid?" do - specify { expect(element_val).to be_valid } - end - - describe "#invalid?" do - specify { expect(element_val).to_not be_invalid } - end - - describe "#too_short?" do - specify { expect(element_val).not_to be_too_short } - end + let(:element_val) { value("1.23") } + let(:valid_str) { "999" } + let(:invalid_str) { "ABC" } + include_examples "global_element_types_non_empty" describe "#too_long?" do specify { expect(element_val).not_to be_too_long } @@ -259,7 +87,8 @@ def value(x) end describe "#map(&block)" do - specify { expect{|b| element_val.map(&b) }.to yield_with_args("1.23123".to_d) } + specify { expect{|b| element_val.map(&b) }.to yield_with_args("1.23".to_d) } + specify { expect{|b| value("1.23456".to_d).map(&b) }.to yield_with_args("1.23456".to_d) } end describe "#to_s" do @@ -278,49 +107,27 @@ def value(x) end end - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(element_val.inspect).to be_a(String) - end - - it "indicates valid" do - expect(element_val.inspect).to match(/value/) - end - - it "indicates value" do - expect(element_val.inspect).to match(/#{element_val.to_s}/) - end - end - - context "when forbidden" do - let(:element_val) do - element_use.copy(:requirement => e_not_used).value("1.00", position) - end - - include_examples "inspect" - end - - context "when required" do - let(:element_val) do - element_use.copy(:requirement => e_mandatory).value("1.00", position) - end - - include_examples "inspect" - end + describe "#to_d" do + specify { expect(value("0").to_d).to eq("0".to_d) } + specify { expect(value("0.25").to_d).to eq("0.25".to_d) } + specify { expect(value("13.1").to_d).to eq("13.1".to_d) } + specify { expect(value("-1.9").to_d).to eq("-1.9".to_d) } + end - context "when optional" do - let(:element_val) do - element_use.copy(:requirement => e_optional).value("1.00", position) - end + describe "#to_f" do + specify { expect(value("0").to_f).to eq(0) } + specify { expect(value("0.25").to_f).to eq(0.25) } + specify { expect(value("13.1").to_f).to eq(13.1) } + specify { expect(value("-1.9").to_f).to eq(-1.9) } + end - include_examples "inspect" - end + describe "#to_i" do + specify { expect(value("0").to_i).to eq(0) } + specify { expect(value("0.25").to_i).to eq(0) } + specify { expect(value("13.1").to_i).to eq(13) } + specify { expect(value("-1.9").to_i).to eq(-1) } end - todo "#to_d" - todo "#to_f" - todo "#to_i" todo "#to_r" describe "#to_x12(truncate)" do @@ -357,6 +164,33 @@ def value(x) end context "without truncation" do + specify { expect(value("0").to_x12(false)).to eq("0000") } + specify { expect(value("1").to_x12(false)).to eq("0001") } + specify { expect(value("20").to_x12(false)).to eq("0020") } + specify { expect(value("300").to_x12(false)).to eq("0300") } + specify { expect(value("4000").to_x12(false)).to eq("4000") } + specify { expect(value("50000").to_x12(false)).to eq("50000") } + specify { expect(value("600000").to_x12(false)).to eq("600000") } + specify { expect(value("7000000").to_x12(false)).to eq("7000000") } + + specify { expect(value("-0").to_x12(false)).to eq("0000") } + specify { expect(value("-1").to_x12(false)).to eq("-0001") } + specify { expect(value("-20").to_x12(false)).to eq("-0020") } + specify { expect(value("-300").to_x12(false)).to eq("-0300") } + specify { expect(value("-4000").to_x12(false)).to eq("-4000") } + specify { expect(value("-50000").to_x12(false)).to eq("-50000") } + specify { expect(value("-600000").to_x12(false)).to eq("-600000") } + specify { expect(value("-7000000").to_x12(false)).to eq("-7000000") } + + specify { expect(value("0.0").to_x12(false)).to eq("0000") } + specify { expect(value("0.1").to_x12(false)).to eq("00.1") } + specify { expect(value("0.02").to_x12(false)).to eq("0.02") } + specify { expect(value("0.003").to_x12(false)).to eq("0000") } # precision is only 2 + + specify { expect(value("-15.0").to_x12(false)).to eq("-0015") } + specify { expect(value("-15.1").to_x12(false)).to eq("-15.1") } + specify { expect(value("-15.02").to_x12(false)).to eq("-15.02") } + specify { expect(value("-15.003").to_x12(false)).to eq("-0015") } end end diff --git a/spec/lib/stupidedi/versions/common/element_types/tm_spec.rb b/spec/lib/stupidedi/versions/common/element_types/tm_spec.rb index 547d1cde0..35225fde1 100644 --- a/spec/lib/stupidedi/versions/common/element_types/tm_spec.rb +++ b/spec/lib/stupidedi/versions/common/element_types/tm_spec.rb @@ -59,7 +59,34 @@ def value_8(x) end context "when given an invalid String" do - specify { expect(value_4("99")).to be_invalid } + specify { expect(value_4("12")).to be_invalid } + specify { expect(value_4("12:30")).to be_invalid } + specify { expect(value_4("2530")).to be_invalid } + end + + context "when given a valid numeric String (hhmm)" do + let(:element_val) { value_4("1230") } + specify { expect(element_val).to be_valid } + specify { expect(element_val).to be_valid } + specify { expect(element_val.hour).to eq(12) } + specify { expect(element_val.minute).to eq(30) } + specify { expect(element_val.second).to be_nil } + end + + context "when given a valid numeric String (hhmmss)" do + let(:element_val) { value_4("123059") } + specify { expect(element_val).to be_valid } + specify { expect(element_val.hour).to eq(12) } + specify { expect(element_val.minute).to eq(30) } + specify { expect(element_val.second).to eq(59) } + end + + context "when given a numeric String (hhmmss.dd)" do + let(:element_val) { value_4("123059999") } + specify { expect(element_val).to be_valid } + specify { expect(element_val.hour).to eq(12) } + specify { expect(element_val.minute).to eq(30) } + specify { expect(element_val.second).to eq("59.999".to_d) } end context "when given a wrong type of value" do @@ -97,174 +124,33 @@ def value_8(x) context "::Invalid" do let(:invalid_val) { value_4("99") } + let(:element_use) { element_use_4 } + include_examples "global_element_types_invalid" describe "#time?" do specify { expect(invalid_val).to be_time } end - - describe "#too_short?" do - specify { expect(invalid_val).to_not be_too_short } - end - - describe "#too_long?" do - specify { expect(invalid_val).to_not be_too_long } - end - - describe "#empty?" do - specify { expect(invalid_val).to_not be_empty } - end - - describe "#valid?" do - specify { expect(invalid_val).to_not be_valid } - end - - describe "#invalid?" do - specify { expect(invalid_val).to be_invalid } - end - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(invalid_val.inspect).to be_a(String) - end - - it "indicates invalid " do - expect(invalid_val.inspect).to match(/invalid/) - end - end - - context "when forbidden" do - let(:invalid_val) do - element_use_6.copy(:requirement => e_not_used).value("99", position) - end - - include_examples "inspect" - end - - context "when required" do - let(:invalid_val) do - element_use_6.copy(:requirement => e_mandatory).value("99", position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:invalid_val) do - element_use_6.copy(:requirement => e_optional).value("99", position) - end - - include_examples "inspect" - end - end - - describe "#to_s" do - specify { expect(invalid_val.to_s).to eq("") } - end - - describe "#to_x12" do - context "with truncation" do - specify { expect(invalid_val.to_x12(true)).to eq("") } - end - - context "without truncation" do - specify { expect(invalid_val.to_x12(false)).to eq("") } - end - end - - todo "#==(other)" - - todo "#copy(changes)" end context "::Empty" do - let(:empty_val) { value_6("") } + let(:empty_val) { value_6("") } + let(:valid_str) { "123059" } + let(:invalid_str) { "ABC" } + let(:element_use) { element_use_6 } + include_examples "global_element_types_empty" describe "#time?" do specify { expect(empty_val).to be_time } end - - describe "#too_short?" do - specify { expect(empty_val).to_not be_too_short } - end - - describe "#too_long?" do - specify { expect(empty_val).to_not be_too_long } - end - - describe "#empty?" do - specify { expect(empty_val).to be_empty } - end - - describe "#valid?" do - specify { expect(empty_val).to be_valid } - end - - describe "#invalid?" do - specify { expect(empty_val).to_not be_invalid } - end - - describe "#inspect" do - shared_examples "inspect" do - it "returns a String" do - expect(empty_val.inspect).to be_a(String) - end - - it "indicates empty " do - expect(empty_val.inspect).to match(/empty/) - end - end - - context "when forbidden" do - let(:empty_val) do - element_use_6.copy(:requirement => e_not_used).empty(position) - end - - include_examples "inspect" - end - - context "when required" do - let(:empty_val) do - element_use_6.copy(:requirement => e_mandatory).empty(position) - end - - include_examples "inspect" - end - - context "when optional" do - let(:empty_val) do - element_use_6.copy(:requirement => e_optional).empty(position) - end - - include_examples "inspect" - end - end - - describe "#to_s" do - specify { expect(empty_val.to_s).to eq("") } - end - - describe "#to_x12" do - context "with truncation" do - specify { expect(empty_val.to_x12(true)).to eq("") } - end - - context "without truncation" do - specify { expect(empty_val.to_x12(false)).to eq("") } - end - end - - describe "#==(other)" do - todo { expect(empty_val).to eq("") } - specify { expect(empty_val).to eq(nil) } - end - - todo "#map(&block)" - - todo "#copy(changes)" end context "::NonEmpty" do + let(:element_val) { value_6("124559") } + let(:element_use) { element_use_6 } + let(:invalid_str) { "wrong" } + let(:valid_str) { "123000" } + include_examples "global_element_types_non_empty" + describe "#time?" do specify { expect(value_4("1230")).to be_time } specify { expect(value_6("123000")).to be_time } @@ -349,12 +235,13 @@ def value_8(x) end describe "#to_s" do - context "when minutes are missing" do - todo { expect(value_6("12").to_s).to eq("12mmss") } + context "when seconds are missing" do + specify { expect(value_6("1230").to_s).to eq("12:30:ss") } end - context "when seconds are missing" do - specify { expect(value_6("1230").to_s).to eq("1230ss") } + context "when seconds are present" do + specify { expect(value_6("123045").to_s).to eq("12:30:45") } + specify { expect(value_6("12304599").to_s).to eq("12:30:45.99") } end end @@ -420,8 +307,6 @@ def value_8(x) specify { expect(value_4("115959").to_time(date, 30, 30).sec).to eq(59) } end end - - todo "when second and minute are nil" end describe "#==(other)" do @@ -457,7 +342,8 @@ def value_8(x) end context "when given a Time" do - specify { expect(value_6("115959")).to_not eq(Time.utc(2020, 12, 31, 11, 59, 59)) } + specify { expect(value_6("115959")).to eq(Time.utc(2020, 12, 31, 11, 59, 59)) } + specify { expect(value_6("115959")).to_not eq(Time.utc(2020, 12, 31, 12, 59, 59)) } end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f5dcbdb62..af32dced9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -8,10 +8,8 @@ require "pp" require "ostruct" -begin - Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each do |file| - require file - end +Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each do |file| + require file end RSpec.configure do |config| diff --git a/spec/support/shared_specs/shared_specs_for_element_types.rb b/spec/support/shared_specs/shared_specs_for_element_types.rb new file mode 100644 index 000000000..3b888f4b4 --- /dev/null +++ b/spec/support/shared_specs/shared_specs_for_element_types.rb @@ -0,0 +1,301 @@ +RSpec.shared_examples "global_element_types_invalid" do + describe "#too_short?" do + specify { expect(invalid_val).to_not be_too_short } + end + + describe "#too_long?" do + specify { expect(invalid_val).to_not be_too_long } + end + + describe "#empty?" do + specify { expect(invalid_val).to_not be_empty } + end + + describe "#blank?" do + specify { expect(invalid_val).to_not be_blank } + end + + describe "#valid?" do + specify { expect(invalid_val).to_not be_valid } + end + + describe "#invalid?" do + specify { expect(invalid_val).to be_invalid } + end + + describe "#usage" do + specify { expect(invalid_val.usage).to eql(element_use) } + end + + describe "#position" do + specify { expect(invalid_val.position).to eql(position) } + end + + describe "#inspect" do + context "when optional" do + let(:invalid_use_) { invalid_val.usage.copy(:requirement => e_optional) } + let(:invalid_val_) { invalid_use_.value(invalid_val.value, position) } + + specify { expect(invalid_val_.inspect).to be_a(String) } + specify { expect(invalid_val_.inspect).to match(/invalid/) } + end + + context "when required" do + let(:invalid_use_) { invalid_val.usage.copy(:requirement => e_required) } + let(:invalid_val_) { invalid_use_.value(invalid_val.value, position) } + + specify { expect(invalid_val_.inspect).to be_a(String) } + specify { expect(invalid_val_.inspect).to match(/invalid/) } + end + + context "when forbidden" do + let(:invalid_use_) { invalid_val.usage.copy(:requirement => e_not_used) } + let(:invalid_val_) { invalid_use_.value(invalid_val.value, position) } + + specify { expect(invalid_val_.inspect).to be_a(String) } + specify { expect(invalid_val_.inspect).to match(/invalid/) } + end + end + + describe "#to_s" do + specify { expect(invalid_val.to_s).to eq("") } + end + + describe "#to_x12(truncate)" do + context "with truncation" do + specify { expect(invalid_val.to_x12(true)).to eq("") } + end + + context "without truncation" do + specify { expect(invalid_val.to_x12(true)).to eq("") } + end + end + + describe "#copy(changes)" do + specify { expect(invalid_val.copy).to eql(invalid_val) } + specify { expect(invalid_val.copy(:usage => nil)).to eql(invalid_val) } + specify { expect(invalid_val.copy(:position => nil)).to eql(invalid_val) } + end + + describe "#coerce(other)" do + specify { expect(invalid_val).to_not respond_to(:coerce) } + end + + describe "#map(&block)" do + specify { expect{|b| invalid_val.map(&b) }.to_not yield_control } + end + + describe "#==(other)" do + specify { expect(invalid_val).to_not eq("") } + specify { expect(invalid_val).to_not eq(nil) } + specify { expect(invalid_val).to eq(invalid_val) } + end +end + +RSpec.shared_examples "global_element_types_empty" do + describe "#too_short?" do + specify { expect(empty_val).to_not be_too_short } + end + + describe "#too_long?" do + specify { expect(empty_val).to_not be_too_long } + end + + describe "#empty?" do + specify { expect(empty_val).to be_empty } + end + + describe "#blank?" do + specify { expect(empty_val).to be_blank } + end + + describe "#valid?" do + specify { expect(empty_val).to be_valid } + end + + describe "#invalid?" do + specify { expect(empty_val).to be_valid } + end + + describe "#usage" do + specify { expect(empty_val.usage).to eql(element_use) } + end + + describe "#position" do + specify { expect(empty_val.position).to eql(position) } + end + + describe "#inspect" do + context "when optional" do + let(:empty_use_) { empty_val.usage.copy(:requirement => e_optional) } + let(:empty_val_) { empty_use_.value("", position) } + + specify { expect(empty_val_.inspect).to be_a(String) } + specify { expect(empty_val_.inspect).to match(/empty/) } + end + + context "when required" do + let(:empty_use_) { empty_val.usage.copy(:requirement => e_required) } + let(:empty_val_) { empty_use_.value("", position) } + + specify { expect(empty_val_.inspect).to be_a(String) } + specify { expect(empty_val_.inspect).to match(/empty/) } + end + + context "when forbidden" do + let(:empty_use_) { empty_val.usage.copy(:requirement => e_not_used) } + let(:empty_val_) { empty_use_.value("", position) } + + specify { expect(empty_val_.inspect).to be_a(String) } + specify { expect(empty_val_.inspect).to match(/empty/) } + end + end + + describe "#to_s" do + specify { expect(empty_val.to_s).to eq("") } + end + + describe "#to_x12(truncate)" do + context "with truncation" do + specify { expect(empty_val.to_x12(true)).to eq("") } + end + + context "without truncation" do + specify { expect(empty_val.to_x12(false)).to eq("") } + end + end + + describe "#copy(changes)" do + specify { expect(empty_val.copy).to eq(empty_val) } + specify { expect(empty_val.copy(:value => valid_str)).to be_valid } + specify { expect(empty_val.copy(:value => valid_str)).to_not be_empty } + specify { expect(empty_val.copy(:value => invalid_str)).to be_invalid } + end + + describe "#coerce(other)" do + specify do + he, me = empty_val.coerce(valid_str) + expect(he).to be_valid + expect(me).to eql(empty_val) + end + end + + describe "#map(&block)" do + specify { expect{|b| empty_val.map(&b) }.to yield_with_args(empty_val.value) } + specify { expect(empty_val.map{|_| valid_str }).to eq(empty_val.usage.value(valid_str, position)) } + end + + describe "#==(other)" do + specify { expect(empty_val).to eq(empty_val) } + specify { expect(empty_val).to eq(empty_val.value) } + specify { expect(empty_val).to_not eq(empty_val.copy(:value => valid_str)) } + specify { expect(empty_val).to eq(empty_val.to_x12) } + end +end + +RSpec.shared_examples "global_element_types_non_empty" do + describe "#too_short?" do + specify { expect(element_val).to_not be_too_short } + end + + describe "#too_long?" do + specify { expect(element_val).to_not be_too_long } + end + + describe "#empty?" do + specify { expect(element_val).to_not be_empty } + end + + describe "#blank?" do + specify { expect(element_val).to_not be_blank } + end + + describe "#valid?" do + specify { expect(element_val).to be_valid } + end + + describe "#invalid?" do + specify { expect(element_val).to_not be_invalid } + end + + describe "#usage" do + specify { expect(element_val.usage).to eql(element_use) } + end + + describe "#position" do + specify { expect(element_val.position).to eql(position) } + end + + describe "#inspect" do + let(:inspect_str_) do + if respond_to?(:inspect_str) + inspect_str + else + element_val_.to_s + end + end + + context "when optional" do + let(:element_use_) { element_val.usage.copy(:requirement => e_optional) } + let(:element_val_) { element_use_.value(element_val, element_val.position) } + + specify { expect(element_val_.inspect).to be_a(String) } + specify { expect(element_val_.inspect).to match(/value/) } + specify { expect(element_val_.inspect).to match(inspect_str_) } + end + + context "when required" do + let(:element_use_) { element_val.usage.copy(:requirement => e_required) } + let(:element_val_) { element_use_.value(element_val, element_val.position) } + + specify { expect(element_val_.inspect).to be_a(String) } + specify { expect(element_val_.inspect).to match(/value/) } + specify { expect(element_val_.inspect).to match(inspect_str_) } + end + + context "when forbidden" do + let(:element_use_) { element_val.usage.copy(:requirement => e_not_used) } + let(:element_val_) { element_use_.value(element_val, element_val.position) } + + specify { expect(element_val_.inspect).to be_a(String) } + specify { expect(element_val_.inspect).to match(/value/) } + specify { expect(element_val_.inspect).to match(inspect_str_) } + end + end + + describe "#copy(changes)" do + specify { expect(element_val.copy).to eq(element_val) } + specify { expect(element_val.copy(:value => valid_str)).to be_valid } + specify { expect(element_val.copy(:value => invalid_str)).to be_invalid } + end + + describe "#coerce(other)" do + specify do + he, me = element_val.coerce(valid_str) + expect(he).to be_valid + expect(me).to eql(element_val) + end + + specify do + he, me = element_val.coerce(invalid_str) + expect(he).to be_invalid + expect(me).to eql(element_val) + end + end + + describe "#map(&block)" do + specify { expect{|b| element_val.map(&b) }.to yield_control } + specify { expect(element_val.map{|_| valid_str }).to be_valid } + specify { expect(element_val.map{|_| invalid_str }).to be_invalid } + end + + describe "#==(other)" do + specify { expect(element_val).to_not eq(nil) } + specify { expect(element_val).to eq(element_val.to_x12(false)) } + specify do + if element_val.respond_to?(:value) + expect(element_val).to eq(element_val.value) + end + end + end +end