tediparse 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +319 -0
- data/LICENSE +26 -0
- data/README.md +308 -0
- data/Rakefile +33 -0
- data/bin/tediparse +102 -0
- data/doc/Defining.md +97 -0
- data/doc/Generating-Grammars.md +241 -0
- data/doc/Generating.md +332 -0
- data/doc/Navigating.md +670 -0
- data/doc/Parsing.md +133 -0
- data/doc/README.md +51 -0
- data/doc/Serializing.md +100 -0
- data/doc/Tokenizing.md +136 -0
- data/doc/Validating.md +140 -0
- data/lib/ruby/array.rb +226 -0
- data/lib/ruby/blank.rb +52 -0
- data/lib/ruby/exception.rb +14 -0
- data/lib/ruby/hash.rb +14 -0
- data/lib/ruby/module.rb +60 -0
- data/lib/ruby/object.rb +56 -0
- data/lib/ruby/string.rb +89 -0
- data/lib/ruby/to_d.rb +82 -0
- data/lib/ruby/to_date.rb +26 -0
- data/lib/ruby/to_time.rb +21 -0
- data/lib/ruby/try.rb +46 -0
- data/lib/stupidedi/builder.rb +6 -0
- data/lib/stupidedi/color.rb +94 -0
- data/lib/stupidedi/config/code_list_config.rb +45 -0
- data/lib/stupidedi/config/functional_group_config.rb +72 -0
- data/lib/stupidedi/config/interchange_config.rb +86 -0
- data/lib/stupidedi/config/transaction_set_config.rb +73 -0
- data/lib/stupidedi/config.rb +100 -0
- data/lib/stupidedi/either.rb +286 -0
- data/lib/stupidedi/exceptions/invalid_element_error.rb +7 -0
- data/lib/stupidedi/exceptions/invalid_schema_error.rb +7 -0
- data/lib/stupidedi/exceptions/missing_grammar_error.rb +50 -0
- data/lib/stupidedi/exceptions/output_error.rb +7 -0
- data/lib/stupidedi/exceptions/parse_error.rb +7 -0
- data/lib/stupidedi/exceptions/stupidedi_error.rb +7 -0
- data/lib/stupidedi/exceptions/tokenize_error.rb +7 -0
- data/lib/stupidedi/exceptions/zipper_error.rb +7 -0
- data/lib/stupidedi/exceptions.rb +13 -0
- data/lib/stupidedi/inspect.rb +26 -0
- data/lib/stupidedi/interchanges/element_types/separator_val.rb +79 -0
- data/lib/stupidedi/interchanges/element_types/special_val.rb +48 -0
- data/lib/stupidedi/interchanges/element_types.rb +11 -0
- data/lib/stupidedi/interchanges.rb +21 -0
- data/lib/stupidedi/parser/builder_dsl.rb +292 -0
- data/lib/stupidedi/parser/constraint_table.rb +566 -0
- data/lib/stupidedi/parser/generation.rb +147 -0
- data/lib/stupidedi/parser/identifier_stack.rb +248 -0
- data/lib/stupidedi/parser/instruction.rb +112 -0
- data/lib/stupidedi/parser/instruction_table.rb +222 -0
- data/lib/stupidedi/parser/navigation.rb +786 -0
- data/lib/stupidedi/parser/state_machine.rb +62 -0
- data/lib/stupidedi/parser/states/abstract_state.rb +385 -0
- data/lib/stupidedi/parser/states/failure_state.rb +69 -0
- data/lib/stupidedi/parser/states/functional_group_state.rb +101 -0
- data/lib/stupidedi/parser/states/initial_state.rb +62 -0
- data/lib/stupidedi/parser/states/interchange_state.rb +96 -0
- data/lib/stupidedi/parser/states/loop_state.rb +74 -0
- data/lib/stupidedi/parser/states/table_state.rb +90 -0
- data/lib/stupidedi/parser/states/transaction_set_state.rb +120 -0
- data/lib/stupidedi/parser/states/transmission_state.rb +59 -0
- data/lib/stupidedi/parser/tokenization.rb +195 -0
- data/lib/stupidedi/parser.rb +32 -0
- data/lib/stupidedi/reader/input/abstract_input.rb +137 -0
- data/lib/stupidedi/reader/input/delegated_input.rb +112 -0
- data/lib/stupidedi/reader/input/file_input.rb +157 -0
- data/lib/stupidedi/reader/input.rb +31 -0
- data/lib/stupidedi/reader/position.rb +78 -0
- data/lib/stupidedi/reader/result.rb +172 -0
- data/lib/stupidedi/reader/segment_dict.rb +176 -0
- data/lib/stupidedi/reader/separators.rb +90 -0
- data/lib/stupidedi/reader/stream_reader.rb +173 -0
- data/lib/stupidedi/reader/token_reader.rb +465 -0
- data/lib/stupidedi/reader/tokens/component_element_tok.rb +71 -0
- data/lib/stupidedi/reader/tokens/composite_element_tok.rb +85 -0
- data/lib/stupidedi/reader/tokens/repeated_element_tok.rb +74 -0
- data/lib/stupidedi/reader/tokens/segment_tok.rb +74 -0
- data/lib/stupidedi/reader/tokens/simple_element_tok.rb +76 -0
- data/lib/stupidedi/reader.rb +121 -0
- data/lib/stupidedi/schema/abstract_def.rb +76 -0
- data/lib/stupidedi/schema/abstract_element_def.rb +35 -0
- data/lib/stupidedi/schema/abstract_element_use.rb +47 -0
- data/lib/stupidedi/schema/abstract_use.rb +79 -0
- data/lib/stupidedi/schema/code_list.rb +99 -0
- data/lib/stupidedi/schema/component_element_use.rb +76 -0
- data/lib/stupidedi/schema/composite_element_def.rb +103 -0
- data/lib/stupidedi/schema/composite_element_use.rb +78 -0
- data/lib/stupidedi/schema/element_req.rb +57 -0
- data/lib/stupidedi/schema/functional_group_def.rb +124 -0
- data/lib/stupidedi/schema/generation/definition_generator.rb +139 -0
- data/lib/stupidedi/schema/generation/element_generator.rb +221 -0
- data/lib/stupidedi/schema/generation/flat_file_reader.rb +551 -0
- data/lib/stupidedi/schema/generation/functional_group_generator.rb +64 -0
- data/lib/stupidedi/schema/generation/interchange_generator.rb +145 -0
- data/lib/stupidedi/schema/generation/master_loader_generator.rb +121 -0
- data/lib/stupidedi/schema/generation/models.rb +85 -0
- data/lib/stupidedi/schema/generation/module_loader_generator.rb +64 -0
- data/lib/stupidedi/schema/generation/registration_generator.rb +230 -0
- data/lib/stupidedi/schema/generation/runner.rb +161 -0
- data/lib/stupidedi/schema/generation/segment_generator.rb +130 -0
- data/lib/stupidedi/schema/generation/support.rb +78 -0
- data/lib/stupidedi/schema/generation/support_modules_generator.rb +126 -0
- data/lib/stupidedi/schema/generation/version_modules.rb +35 -0
- data/lib/stupidedi/schema/generation.rb +94 -0
- data/lib/stupidedi/schema/interchange_def.rb +103 -0
- data/lib/stupidedi/schema/loop_def.rb +156 -0
- data/lib/stupidedi/schema/repeat_count.rb +86 -0
- data/lib/stupidedi/schema/segment_def.rb +122 -0
- data/lib/stupidedi/schema/segment_req.rb +46 -0
- data/lib/stupidedi/schema/segment_use.rb +99 -0
- data/lib/stupidedi/schema/simple_element_def.rb +51 -0
- data/lib/stupidedi/schema/simple_element_use.rb +83 -0
- data/lib/stupidedi/schema/syntax_note.rb +52 -0
- data/lib/stupidedi/schema/table_def.rb +178 -0
- data/lib/stupidedi/schema/transaction_set_def.rb +125 -0
- data/lib/stupidedi/schema.rb +30 -0
- data/lib/stupidedi/sets.rb +42 -0
- data/lib/stupidedi/transaction_sets/builder/dsl.rb +192 -0
- data/lib/stupidedi/transaction_sets/builder.rb +188 -0
- data/lib/stupidedi/transaction_sets/common/implementations/element_reqs.rb +37 -0
- data/lib/stupidedi/transaction_sets/common/implementations/segment_reqs.rb +31 -0
- data/lib/stupidedi/transaction_sets/common/implementations.rb +11 -0
- data/lib/stupidedi/transaction_sets/common.rb +8 -0
- data/lib/stupidedi/transaction_sets/validation/ambiguity.rb +395 -0
- data/lib/stupidedi/transaction_sets/validation/implementation.rb +12 -0
- data/lib/stupidedi/transaction_sets/validation.rb +9 -0
- data/lib/stupidedi/transaction_sets.rb +25 -0
- data/lib/stupidedi/values/abstract_element_val.rb +19 -0
- data/lib/stupidedi/values/abstract_val.rb +133 -0
- data/lib/stupidedi/values/composite_element_val.rb +102 -0
- data/lib/stupidedi/values/functional_group_val.rb +105 -0
- data/lib/stupidedi/values/interchange_val.rb +102 -0
- data/lib/stupidedi/values/invalid_envelope_val.rb +61 -0
- data/lib/stupidedi/values/invalid_segment_val.rb +89 -0
- data/lib/stupidedi/values/loop_val.rb +73 -0
- data/lib/stupidedi/values/repeated_element_val.rb +113 -0
- data/lib/stupidedi/values/segment_val.rb +105 -0
- data/lib/stupidedi/values/segment_val_group.rb +20 -0
- data/lib/stupidedi/values/simple_element_val.rb +80 -0
- data/lib/stupidedi/values/table_val.rb +69 -0
- data/lib/stupidedi/values/transaction_set_val.rb +69 -0
- data/lib/stupidedi/values/transmission_val.rb +56 -0
- data/lib/stupidedi/values.rb +22 -0
- data/lib/stupidedi/version.rb +4 -0
- data/lib/stupidedi/versions/common/element_reqs.rb +13 -0
- data/lib/stupidedi/versions/common/element_types/an.rb +386 -0
- data/lib/stupidedi/versions/common/element_types/dt.rb +572 -0
- data/lib/stupidedi/versions/common/element_types/id.rb +304 -0
- data/lib/stupidedi/versions/common/element_types/nn.rb +312 -0
- data/lib/stupidedi/versions/common/element_types/operators.rb +128 -0
- data/lib/stupidedi/versions/common/element_types/r.rb +342 -0
- data/lib/stupidedi/versions/common/element_types/simple_element.rb +73 -0
- data/lib/stupidedi/versions/common/element_types/tm.rb +347 -0
- data/lib/stupidedi/versions/common/element_types.rb +29 -0
- data/lib/stupidedi/versions/common/segment_reqs.rb +15 -0
- data/lib/stupidedi/versions/common/syntax_notes.rb +172 -0
- data/lib/stupidedi/versions/common.rb +11 -0
- data/lib/stupidedi/versions.rb +25 -0
- data/lib/stupidedi/writer/claredi.rb +178 -0
- data/lib/stupidedi/writer/default.rb +119 -0
- data/lib/stupidedi/writer.rb +7 -0
- data/lib/stupidedi/zipper/abstract_cursor.rb +351 -0
- data/lib/stupidedi/zipper/dangling_cursor.rb +103 -0
- data/lib/stupidedi/zipper/edited_cursor.rb +157 -0
- data/lib/stupidedi/zipper/memoized_cursor.rb +133 -0
- data/lib/stupidedi/zipper/path.rb +132 -0
- data/lib/stupidedi/zipper/root_cursor.rb +120 -0
- data/lib/stupidedi/zipper/stack_cursor.rb +107 -0
- data/lib/stupidedi/zipper.rb +45 -0
- data/lib/stupidedi.rb +69 -0
- data/lib/tediparse.rb +1 -0
- metadata +249 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Versions
|
|
6
|
+
module Common
|
|
7
|
+
module ElementTypes
|
|
8
|
+
module Operators
|
|
9
|
+
module Unary
|
|
10
|
+
def unary_operators(*ops)
|
|
11
|
+
file, line, = Stupidedi.caller
|
|
12
|
+
|
|
13
|
+
ops.each do |op|
|
|
14
|
+
class_eval(<<-RUBY, file, line.to_i - 1)
|
|
15
|
+
def #{op}(&block)
|
|
16
|
+
copy(:value => value.#{op}(&block))
|
|
17
|
+
end
|
|
18
|
+
RUBY
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module Binary
|
|
24
|
+
def binary_operators(*ops)
|
|
25
|
+
file, line, = Stupidedi.caller
|
|
26
|
+
|
|
27
|
+
if ops.last.is_a?(Hash)
|
|
28
|
+
options = ops.pop
|
|
29
|
+
|
|
30
|
+
case options[:coerce]
|
|
31
|
+
when String, Symbol
|
|
32
|
+
coerce = options[:coerce]
|
|
33
|
+
else
|
|
34
|
+
raise ArgumentError,
|
|
35
|
+
"must pass :coerce => :method"
|
|
36
|
+
end
|
|
37
|
+
else
|
|
38
|
+
raise ArgumentError,
|
|
39
|
+
"must pass :coerce => :method"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Note that we can't test respond_to?(coerce) because coerce is
|
|
43
|
+
# often a refinement method, like #to_d, #to_time, etc. Currently
|
|
44
|
+
# respond_to? returns false on refinment methods, so we just call
|
|
45
|
+
# the method and assume a NoMethodError is due to coerce (hopefully
|
|
46
|
+
# not some other missing method)
|
|
47
|
+
ops.each do |op|
|
|
48
|
+
class_eval(<<-RUBY, file, line.to_i - 1)
|
|
49
|
+
def #{op}(other, &block)
|
|
50
|
+
begin
|
|
51
|
+
copy(:value => value.#{op}(other.#{coerce}, &block))
|
|
52
|
+
rescue NoMethodError
|
|
53
|
+
begin
|
|
54
|
+
me, he = other.coerce(self)
|
|
55
|
+
copy(:value => me.#{op}(he, &block).#{coerce})
|
|
56
|
+
rescue NoMethodError
|
|
57
|
+
raise TypeError,
|
|
58
|
+
"cannot coerce \#{other.class} to \#{self.class}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
RUBY
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
module Relational
|
|
68
|
+
def relational_operators(*ops)
|
|
69
|
+
file, line, = Stupidedi.caller
|
|
70
|
+
|
|
71
|
+
if ops.last.is_a?(Hash)
|
|
72
|
+
options = ops.pop
|
|
73
|
+
|
|
74
|
+
case options[:coerce]
|
|
75
|
+
when String, Symbol
|
|
76
|
+
coerce = options[:coerce]
|
|
77
|
+
else
|
|
78
|
+
raise ArgumentError,
|
|
79
|
+
"must pass :coerce => :method"
|
|
80
|
+
end
|
|
81
|
+
else
|
|
82
|
+
raise ArgumentError,
|
|
83
|
+
"must pass :coerce => :method"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Note that we can't test respond_to?(coerce) because coerce is
|
|
87
|
+
# often a refinement method, like #to_d, #to_time, etc. Currently
|
|
88
|
+
# respond_to? returns false on refinment methods, so we just call
|
|
89
|
+
# the method and assume a NoMethodError is due to coerce (hopefully
|
|
90
|
+
# not some other missing method)
|
|
91
|
+
ops.each do |op|
|
|
92
|
+
class_eval(<<-RUBY, file, line.to_i - 1)
|
|
93
|
+
def #{op}(other, &block)
|
|
94
|
+
begin
|
|
95
|
+
value.#{op}(other.#{coerce}, &block)
|
|
96
|
+
rescue NoMethodError
|
|
97
|
+
begin
|
|
98
|
+
me, he = other.coerce(self)
|
|
99
|
+
me.#{op}(he, &block)
|
|
100
|
+
rescue NoMethodError
|
|
101
|
+
raise TypeError,
|
|
102
|
+
"cannot coerce \#{other.class} to \#{self.class}"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
RUBY
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
module Wrappers
|
|
112
|
+
def wrappers(*ops)
|
|
113
|
+
file, line, = Stupidedi.caller
|
|
114
|
+
|
|
115
|
+
ops.each do |op|
|
|
116
|
+
class_eval(<<-RUBY, file, line.to_i - 1)
|
|
117
|
+
def #{op}(*args, &block)
|
|
118
|
+
copy(:value => value.#{op}(*args, &block))
|
|
119
|
+
end
|
|
120
|
+
RUBY
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Versions
|
|
6
|
+
module Common
|
|
7
|
+
module ElementTypes
|
|
8
|
+
class R < SimpleElementDef
|
|
9
|
+
# @return [Integer]
|
|
10
|
+
attr_reader :max_precision
|
|
11
|
+
|
|
12
|
+
def initialize(id, name, min_length, max_length, max_precision = nil, description = nil, parent = nil)
|
|
13
|
+
super(id, name, min_length, max_length, description, parent)
|
|
14
|
+
|
|
15
|
+
if max_precision.try(:>, max_length)
|
|
16
|
+
raise Exceptions::InvalidSchemaError,
|
|
17
|
+
"max_precision cannot be greater than max_length"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@max_precision = max_precision
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [R]
|
|
24
|
+
def copy(changes = {})
|
|
25
|
+
R.new \
|
|
26
|
+
changes.fetch(:id, @id),
|
|
27
|
+
changes.fetch(:name, @name),
|
|
28
|
+
changes.fetch(:min_length, @min_length),
|
|
29
|
+
changes.fetch(:max_length, @max_length),
|
|
30
|
+
changes.fetch(:max_precision, @max_precision),
|
|
31
|
+
changes.fetch(:description, @description),
|
|
32
|
+
changes.fetch(:parent, @parent)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def companion
|
|
36
|
+
FloatVal
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
#
|
|
41
|
+
# @see X222.pdf B.1.1.3.1.2 Decimal
|
|
42
|
+
#
|
|
43
|
+
class FloatVal < Values::SimpleElementVal
|
|
44
|
+
def numeric?
|
|
45
|
+
true
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def too_long?
|
|
49
|
+
false
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def too_short?
|
|
53
|
+
false
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class Invalid < FloatVal
|
|
57
|
+
# @return [Object]
|
|
58
|
+
attr_reader :value
|
|
59
|
+
|
|
60
|
+
def initialize(value, usage, position)
|
|
61
|
+
@value = value
|
|
62
|
+
super(usage, position)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def valid?
|
|
66
|
+
false
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def empty?
|
|
70
|
+
false
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @return [FloatVal]
|
|
74
|
+
def map
|
|
75
|
+
self
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @return [String]
|
|
79
|
+
# :nocov:
|
|
80
|
+
def inspect
|
|
81
|
+
id = definition.try do |d|
|
|
82
|
+
"[#{"% 5s" % d.id}: #{d.name}]".bind do |s|
|
|
83
|
+
if usage.forbidden?
|
|
84
|
+
ansi.forbidden(s)
|
|
85
|
+
elsif usage.required?
|
|
86
|
+
ansi.required(s)
|
|
87
|
+
else
|
|
88
|
+
ansi.optional(s)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
ansi.element(" R.invalid#{id}") + "(#{ansi.invalid(@value.inspect)})"
|
|
94
|
+
end
|
|
95
|
+
# :nocov:
|
|
96
|
+
|
|
97
|
+
# @return [String]
|
|
98
|
+
def to_s
|
|
99
|
+
""
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# @return [String]
|
|
103
|
+
def to_x12(truncate = true)
|
|
104
|
+
""
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# @return [Boolean]
|
|
108
|
+
def ==(other)
|
|
109
|
+
eql?(other)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# @return [Invalid]
|
|
113
|
+
def copy(changes = {})
|
|
114
|
+
self
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
class Valid < FloatVal
|
|
119
|
+
def valid?
|
|
120
|
+
true
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def coerce(other)
|
|
124
|
+
return FloatVal.value(other, usage, position), self
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# @return [Empty]
|
|
128
|
+
def copy(changes = {})
|
|
129
|
+
FloatVal.value \
|
|
130
|
+
changes.fetch(:value, value),
|
|
131
|
+
changes.fetch(:usage, usage),
|
|
132
|
+
changes.fetch(:position, position)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# @return [FloatVal]
|
|
136
|
+
def map
|
|
137
|
+
FloatVal.value(yield(value), usage, position)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @return [Boolean]
|
|
141
|
+
def ==(other)
|
|
142
|
+
other = FloatVal.value(other, usage, position)
|
|
143
|
+
other.valid? and other.value == value
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
class Empty < Valid
|
|
148
|
+
def value
|
|
149
|
+
nil
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def empty?
|
|
153
|
+
true
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# @return [String]
|
|
157
|
+
# :nocov:
|
|
158
|
+
def inspect
|
|
159
|
+
id = definition.try do |d|
|
|
160
|
+
"[#{"% 5s" % d.id}: #{d.name}]".bind do |s|
|
|
161
|
+
if usage.forbidden?
|
|
162
|
+
ansi.forbidden(s)
|
|
163
|
+
elsif usage.required?
|
|
164
|
+
ansi.required(s)
|
|
165
|
+
else
|
|
166
|
+
ansi.optional(s)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
ansi.element(" R.empty#{id}")
|
|
172
|
+
end
|
|
173
|
+
# :nocov:
|
|
174
|
+
|
|
175
|
+
# @return [String]
|
|
176
|
+
def to_s
|
|
177
|
+
""
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# @return [String]
|
|
181
|
+
def to_x12(truncate = true)
|
|
182
|
+
""
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
class NonEmpty < Valid
|
|
187
|
+
# @group Mathematical Operators
|
|
188
|
+
#################################################################
|
|
189
|
+
|
|
190
|
+
extend Operators::Binary
|
|
191
|
+
binary_operators :+, :-, :*, :/, :%, :coerce => :to_d
|
|
192
|
+
|
|
193
|
+
extend Operators::Unary
|
|
194
|
+
unary_operators :abs, :-@, :+@
|
|
195
|
+
|
|
196
|
+
extend Operators::Relational
|
|
197
|
+
relational_operators :<, :>, :<=, :>=, :<=>, :coerce => :to_d
|
|
198
|
+
|
|
199
|
+
# @endgroup
|
|
200
|
+
#################################################################
|
|
201
|
+
|
|
202
|
+
# @return [BigDecimal]
|
|
203
|
+
attr_reader :value
|
|
204
|
+
|
|
205
|
+
def_delegators :value, :to_i, :to_d, :to_f, :to_r, :to_c
|
|
206
|
+
|
|
207
|
+
def initialize(value, usage, position)
|
|
208
|
+
@value = value
|
|
209
|
+
super(usage, position)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def empty?
|
|
213
|
+
false
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# @return [String]
|
|
217
|
+
# :nocov:
|
|
218
|
+
def inspect
|
|
219
|
+
id = definition.try do |d|
|
|
220
|
+
"[#{"% 5s" % d.id}: #{d.name}]".bind do |s|
|
|
221
|
+
if usage.forbidden?
|
|
222
|
+
ansi.forbidden(s)
|
|
223
|
+
elsif usage.required?
|
|
224
|
+
ansi.required(s)
|
|
225
|
+
else
|
|
226
|
+
ansi.optional(s)
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
ansi.element(" R.value#{id}") + "(#{to_s})"
|
|
232
|
+
end
|
|
233
|
+
# :nocov:
|
|
234
|
+
|
|
235
|
+
# @return [String]
|
|
236
|
+
def to_s
|
|
237
|
+
if definition.max_precision.present?
|
|
238
|
+
@value.round(definition.max_precision).to_s("F")
|
|
239
|
+
else
|
|
240
|
+
@value.to_s("F")
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# While the ASC X12 standard supports the usage of exponential
|
|
245
|
+
# notation, the HIPAA guides prohibit it. In the interest of
|
|
246
|
+
# simplicity, this method will not output exponential notation,
|
|
247
|
+
# as there is currently no configuration attribute to indicate
|
|
248
|
+
# if this is allowed or not -- if this is required in the future,
|
|
249
|
+
# the best place for it to fit would be in SimpleElementUse
|
|
250
|
+
#
|
|
251
|
+
# @return [String]
|
|
252
|
+
def to_x12(truncate = true)
|
|
253
|
+
remaining =
|
|
254
|
+
if @value.to_i.zero?
|
|
255
|
+
definition.max_length
|
|
256
|
+
else
|
|
257
|
+
definition.max_length - @value.to_i.abs.to_s.length
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# The integer part consumes all the space, so there is no room
|
|
261
|
+
# for a fractional amount
|
|
262
|
+
if remaining <= 0 and truncate
|
|
263
|
+
int = @value.to_i
|
|
264
|
+
sign = (int < 0) ? "-" : ""
|
|
265
|
+
return sign + int.abs.to_s.take(definition.max_length)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Don't exceed the definition's max_precision
|
|
269
|
+
precision =
|
|
270
|
+
if definition.max_precision.present?
|
|
271
|
+
(definition.max_precision < remaining) ?
|
|
272
|
+
definition.max_precision : remaining
|
|
273
|
+
else
|
|
274
|
+
remaining
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
rounded = @value.round(precision)
|
|
278
|
+
sign = (rounded < 0) ? "-" : ""
|
|
279
|
+
|
|
280
|
+
# Leading zeros preceeding the decimal point and trailing zeros
|
|
281
|
+
# following the decimal point must be supressed unless necessary
|
|
282
|
+
# to satisfy a minimum length requirement or to indicate
|
|
283
|
+
# precision, respectively.
|
|
284
|
+
if rounded.zero?
|
|
285
|
+
"0" * definition.min_length
|
|
286
|
+
else
|
|
287
|
+
base = rounded.abs
|
|
288
|
+
base = base.is_a?(BigDecimal) ? base : BigDecimal(base)
|
|
289
|
+
sign + base.to_s("F").
|
|
290
|
+
gsub(/^0+/, ""). # leading zeros
|
|
291
|
+
gsub(/0+$/, ""). # trailing zeros
|
|
292
|
+
gsub(/\.$/, ""). # trailing decimal point
|
|
293
|
+
rjust(definition.min_length, "0")
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def too_long?
|
|
298
|
+
# We can truncate the fractional portion as much as needed, so
|
|
299
|
+
# the only concern we have about length is regarding the digits
|
|
300
|
+
# to the left of the decimal place.
|
|
301
|
+
|
|
302
|
+
# The length of a decimal type does not include an optional sign
|
|
303
|
+
definition.max_length < @value.to_i.abs.to_s.length
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
class << FloatVal
|
|
309
|
+
# @group Constructors
|
|
310
|
+
###################################################################
|
|
311
|
+
|
|
312
|
+
# @return [FloatVal]
|
|
313
|
+
def empty(usage, position)
|
|
314
|
+
self::Empty.new(usage, position)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# @return [FloatVal]
|
|
318
|
+
def value(object, usage, position)
|
|
319
|
+
if object.is_a?(FloatVal)
|
|
320
|
+
object#.copy(:usage => usage, :position => position)
|
|
321
|
+
elsif object.blank?
|
|
322
|
+
self::Empty.new(usage, position)
|
|
323
|
+
else
|
|
324
|
+
self::NonEmpty.new(object.to_d, usage, position)
|
|
325
|
+
end
|
|
326
|
+
rescue ArgumentError
|
|
327
|
+
self::Invalid.new(object, usage, position)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# @endgroup
|
|
331
|
+
###################################################################
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# Prevent direct instantiation of abstract class FloatVal
|
|
335
|
+
FloatVal.eigenclass.send(:protected, :new)
|
|
336
|
+
FloatVal::Empty.eigenclass.send(:public, :new)
|
|
337
|
+
FloatVal::Invalid.eigenclass.send(:public, :new)
|
|
338
|
+
FloatVal::NonEmpty.eigenclass.send(:public, :new)
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Versions
|
|
6
|
+
module Common
|
|
7
|
+
module ElementTypes
|
|
8
|
+
class SimpleElementDef < Schema::SimpleElementDef
|
|
9
|
+
# @return [Symbol]
|
|
10
|
+
attr_reader :id
|
|
11
|
+
|
|
12
|
+
# @return [String]
|
|
13
|
+
attr_reader :name
|
|
14
|
+
|
|
15
|
+
# @return [String]
|
|
16
|
+
attr_reader :description
|
|
17
|
+
|
|
18
|
+
# @return [Integer]
|
|
19
|
+
attr_reader :min_length
|
|
20
|
+
|
|
21
|
+
# @return [Integer]
|
|
22
|
+
attr_reader :max_length
|
|
23
|
+
|
|
24
|
+
# @return [Schema::SegmentDef, Schema::CompositeElementDef]
|
|
25
|
+
attr_reader :parent
|
|
26
|
+
|
|
27
|
+
# @return [Class<Values::SimpleElementVal>]
|
|
28
|
+
abstract :companion
|
|
29
|
+
|
|
30
|
+
def initialize(id, name, min_length, max_length, description = nil, parent = nil)
|
|
31
|
+
@id, @name, @min_length, @max_length, @description, @parent =
|
|
32
|
+
id, name, min_length, max_length, description, parent
|
|
33
|
+
|
|
34
|
+
if min_length < 1
|
|
35
|
+
raise Exceptions::InvalidSchemaError,
|
|
36
|
+
"min_length must be positive"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if min_length > max_length
|
|
40
|
+
raise Exceptions::InvalidSchemaError,
|
|
41
|
+
"min_length cannot be greater than max_length"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @return [SimpleElementDef]
|
|
46
|
+
def copy(changes = {})
|
|
47
|
+
self.class.new \
|
|
48
|
+
changes.fetch(:id, @id),
|
|
49
|
+
changes.fetch(:name, @name),
|
|
50
|
+
changes.fetch(:min_length, @min_length),
|
|
51
|
+
changes.fetch(:max_length, @max_length),
|
|
52
|
+
changes.fetch(:description, @description),
|
|
53
|
+
changes.fetch(:parent, @parent)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Constructs a SimpleElementVal from the given `value`
|
|
57
|
+
#
|
|
58
|
+
# @return [Values::SimpleElementVal]
|
|
59
|
+
def value(object, usage, position)
|
|
60
|
+
companion.value(object, usage, position)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Constructs an empty SimpleElementVal
|
|
64
|
+
#
|
|
65
|
+
# @return [Values::SimpleElementVal]
|
|
66
|
+
def empty(usage, position)
|
|
67
|
+
companion.empty(usage, position)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|