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,572 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Versions
|
|
6
|
+
module Common
|
|
7
|
+
module ElementTypes
|
|
8
|
+
#
|
|
9
|
+
# Simple element definition for date elements
|
|
10
|
+
#
|
|
11
|
+
# @see DateVal
|
|
12
|
+
# @see X222.pdf B.1.1.3.1.5 Date
|
|
13
|
+
#
|
|
14
|
+
class DT < SimpleElementDef
|
|
15
|
+
def initialize(id, name, min_length, max_length, description = nil, parent = nil)
|
|
16
|
+
super(id, name, min_length, max_length, description, parent)
|
|
17
|
+
|
|
18
|
+
unless min_length == 6 or min_length == 8
|
|
19
|
+
raise Exceptions::InvalidSchemaError,
|
|
20
|
+
"min_length must be either 6 or 8"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
unless max_length == 6 or max_length == 8
|
|
24
|
+
raise Exceptions::InvalidSchemaError,
|
|
25
|
+
"max_length must be either 6 or 8"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def companion
|
|
30
|
+
DateVal
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
# @see X222.pdf B.1.1.3.1.5 Date
|
|
36
|
+
#
|
|
37
|
+
class DateVal < Values::SimpleElementVal
|
|
38
|
+
def date?
|
|
39
|
+
true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def proper?
|
|
43
|
+
false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def too_long?
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def too_short?
|
|
51
|
+
false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
# Models invalid dates, either with a bad format or a date that
|
|
56
|
+
# does not exist (eg Febuary 30th 2000)
|
|
57
|
+
#
|
|
58
|
+
class Invalid < DateVal
|
|
59
|
+
# @return [Object]
|
|
60
|
+
attr_reader :value
|
|
61
|
+
|
|
62
|
+
def initialize(value, usage, position)
|
|
63
|
+
super(usage, position)
|
|
64
|
+
@value = value
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def valid?
|
|
68
|
+
false
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def empty?
|
|
72
|
+
false
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @return [DateVal]
|
|
76
|
+
def map
|
|
77
|
+
self
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @return [String]
|
|
81
|
+
# :nocov:
|
|
82
|
+
def inspect
|
|
83
|
+
id = definition.bind do |d|
|
|
84
|
+
"[#{"% 5s" % d.id}: #{d.name}]".bind do |s|
|
|
85
|
+
if usage.forbidden?
|
|
86
|
+
ansi.forbidden(s)
|
|
87
|
+
elsif usage.required?
|
|
88
|
+
ansi.required(s)
|
|
89
|
+
else
|
|
90
|
+
ansi.optional(s)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
ansi.element("DT.invalid#{id}") + "(#{ansi.invalid(@value.inspect)})"
|
|
96
|
+
end
|
|
97
|
+
# :nocov:
|
|
98
|
+
|
|
99
|
+
# @return [String]
|
|
100
|
+
def to_s
|
|
101
|
+
""
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# @return [String]
|
|
105
|
+
def to_x12(truncate = true)
|
|
106
|
+
""
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# @return [Boolean]
|
|
110
|
+
def ==(other)
|
|
111
|
+
eql?(other)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# @return [Invalid]
|
|
115
|
+
def copy(changes = {})
|
|
116
|
+
self
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
class Valid < DateVal
|
|
121
|
+
def valid?
|
|
122
|
+
true
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @return [DateVal]
|
|
126
|
+
def map
|
|
127
|
+
DateVal.value(yield(value), usage, position)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# @return [DateVal]
|
|
131
|
+
def copy(changes = {})
|
|
132
|
+
DateVal.value \
|
|
133
|
+
changes.fetch(:value, value),
|
|
134
|
+
changes.fetch(:usage, usage),
|
|
135
|
+
changes.fetch(:position, position)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def coerce(other)
|
|
139
|
+
return DateVal.value(other, usage, position), self
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def ==(other)
|
|
143
|
+
other = DateVal.value(other, usage, position)
|
|
144
|
+
other.valid? and other.value == value
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
#
|
|
149
|
+
# Empty date value. Shouldn't be directly instantiated -- instead,
|
|
150
|
+
# use the {DateVal.empty} constructor.
|
|
151
|
+
#
|
|
152
|
+
class Empty < Valid
|
|
153
|
+
def value
|
|
154
|
+
nil
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def empty?
|
|
158
|
+
true
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# @return [String]
|
|
162
|
+
# :nocov:
|
|
163
|
+
def inspect
|
|
164
|
+
id = definition.bind do |d|
|
|
165
|
+
"[#{"% 5s" % d.id}: #{d.name}]".bind do |s|
|
|
166
|
+
if usage.forbidden?
|
|
167
|
+
ansi.forbidden(s)
|
|
168
|
+
elsif usage.required?
|
|
169
|
+
ansi.required(s)
|
|
170
|
+
else
|
|
171
|
+
ansi.optional(s)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
ansi.element("DT.empty#{id}")
|
|
177
|
+
end
|
|
178
|
+
# :nocov:
|
|
179
|
+
|
|
180
|
+
# @return [String]
|
|
181
|
+
def to_s
|
|
182
|
+
""
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# @return [String]
|
|
186
|
+
def to_x12(truncate = true)
|
|
187
|
+
""
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
#
|
|
192
|
+
# Date with a fully-specified year (with century). Shouldn't be
|
|
193
|
+
# directly instantiated -- instead use the {DateVal.value} constructor
|
|
194
|
+
#
|
|
195
|
+
class Proper < Valid
|
|
196
|
+
def_delegators :@value, :year, :month, :day, :cwday, :cweek,
|
|
197
|
+
:downto, :upto, :step, :httpdate, :to_s, :to_i, :strftime,
|
|
198
|
+
:iso8601, :rfc2822, :rfc3339, :rfc822, :leap?, :julian?,
|
|
199
|
+
:gregorian?, :mday, :mon, :to_datetime, :to_int, :to_r, :to_c,
|
|
200
|
+
:wday, :xmlschema, :yday, :start
|
|
201
|
+
|
|
202
|
+
extend Operators::Wrappers
|
|
203
|
+
wrappers :+, :<<, :>>, :next_day, :next_month, :next_year,
|
|
204
|
+
:prev_day, :prev_month, :prev_year
|
|
205
|
+
|
|
206
|
+
extend Operators::Unary
|
|
207
|
+
unary_operators :next, :succ, :prev
|
|
208
|
+
|
|
209
|
+
extend Operators::Relational
|
|
210
|
+
relational_operators :<, :>, :<=, :>=, :<=>, :-, :coerce => :to_date
|
|
211
|
+
|
|
212
|
+
attr_reader :value
|
|
213
|
+
|
|
214
|
+
def initialize(value, usage, position)
|
|
215
|
+
@value = value
|
|
216
|
+
|
|
217
|
+
# Check that date is reasonably valid
|
|
218
|
+
unless @value.year.between?(0, 9999) and @value.month.between?(1, 12) and @value.day.between?(1, 31)
|
|
219
|
+
raise Exceptions::InvalidElementError,
|
|
220
|
+
"invalid date: year(#{year}) month(#{month}) day(#{day})"
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
super(usage, position)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def empty?
|
|
227
|
+
false
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def proper?
|
|
231
|
+
true
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# @return [Date]
|
|
235
|
+
def to_date
|
|
236
|
+
@value
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# @return [Time]
|
|
240
|
+
def to_time(hour = nil, minute = nil, second = nil)
|
|
241
|
+
if minute.nil? and second.nil? and hour.respond_to?(:hour)
|
|
242
|
+
if hour.respond_to?(:minute) and hour.respond_to?(:second)
|
|
243
|
+
hour, minute, second = hour.hour, hour.minute, hour.second
|
|
244
|
+
elsif hour.respond_to?(:min) and hour.respond_to?(:sec)
|
|
245
|
+
hour, minute, second = hour.hour, hour.min, hour.sec
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
if not second.nil?
|
|
250
|
+
Time.utc(year, month, day, hour, minute, second)
|
|
251
|
+
elsif not minute.nil?
|
|
252
|
+
Time.utc(year, month, day, hour, minute)
|
|
253
|
+
elsif not hour.nil?
|
|
254
|
+
Time.utc(year, month, day, hour)
|
|
255
|
+
else
|
|
256
|
+
Time.utc(year, month, day)
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# @return [Proper] self
|
|
261
|
+
def oldest(date)
|
|
262
|
+
self
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# @return [Proper] self
|
|
266
|
+
def newest(date)
|
|
267
|
+
self
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# @return [Proper] self
|
|
271
|
+
def future
|
|
272
|
+
self
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# @return [Proper] self
|
|
276
|
+
def past
|
|
277
|
+
self
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# @return [String]
|
|
281
|
+
# :nocov:
|
|
282
|
+
def inspect
|
|
283
|
+
id = definition.bind do |d|
|
|
284
|
+
"[#{"% 5s" % d.id}: #{d.name}]".bind do |s|
|
|
285
|
+
if usage.forbidden?
|
|
286
|
+
ansi.forbidden(s)
|
|
287
|
+
elsif usage.required?
|
|
288
|
+
ansi.required(s)
|
|
289
|
+
else
|
|
290
|
+
ansi.optional(s)
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
ansi.element("DT.value#{id}") + "(#{"%04d-%02d-%02d" % [year, month, day]})"
|
|
296
|
+
end
|
|
297
|
+
# :nocov:
|
|
298
|
+
|
|
299
|
+
# @return [String]
|
|
300
|
+
def to_x12(truncate = true)
|
|
301
|
+
x12 =
|
|
302
|
+
if definition.max_length < 8
|
|
303
|
+
"%02d%02d%02d" % [year % 100, month, day]
|
|
304
|
+
else
|
|
305
|
+
"%04d%02d%02d" % [year % 10000, month, day]
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
if truncate
|
|
309
|
+
x12.slice(-definition.max_length..-1)
|
|
310
|
+
else
|
|
311
|
+
x12
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def too_long?
|
|
316
|
+
if definition.max_length < 8
|
|
317
|
+
definition.max_length - 2 < year.to_s.length
|
|
318
|
+
else
|
|
319
|
+
definition.max_length - 4 < year.to_s.length
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
#
|
|
325
|
+
# Date with a partially-specified year (two digits, missing century).
|
|
326
|
+
# Shouldn't be directly instantiated -- instead, use the constuctor
|
|
327
|
+
# method {DateVal.value}
|
|
328
|
+
#
|
|
329
|
+
class Improper < Valid
|
|
330
|
+
# @return [Integer]
|
|
331
|
+
attr_reader :year
|
|
332
|
+
|
|
333
|
+
# @return [Integer]
|
|
334
|
+
attr_reader :month
|
|
335
|
+
|
|
336
|
+
# @return [Integer]
|
|
337
|
+
attr_reader :day
|
|
338
|
+
|
|
339
|
+
def initialize(year, month, day, usage, position)
|
|
340
|
+
@year, @month, @day = year, month, day
|
|
341
|
+
|
|
342
|
+
# Check that date is reasonably valid
|
|
343
|
+
unless @year.between?(0, 99) and @month.between?(1, 12) and @day.between?(1, 31)
|
|
344
|
+
raise Exceptions::InvalidElementError,
|
|
345
|
+
"invalid date: year(#{year}) month(#{month}) day(#{day})"
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
super(usage, position)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def value
|
|
352
|
+
[@year, @month, @day]
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def copy(changes = {})
|
|
356
|
+
if [:year, :month, :day].any?{|k| changes.include?(k) }
|
|
357
|
+
changes[:value] = [changes.fetch(:year, @year),
|
|
358
|
+
changes.fetch(:month, @month),
|
|
359
|
+
changes.fetch(:day, @day)]
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
super(changes)
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def empty?
|
|
366
|
+
false
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def proper?
|
|
370
|
+
false
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def too_short?
|
|
374
|
+
# Less than a 4-digit year means our max length is 7, but in
|
|
375
|
+
# practice the definition min/max lengths are either 6 or 8
|
|
376
|
+
definition.min_length > 6
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def too_long?
|
|
380
|
+
# We know month and day occupy four characters, but year *could*
|
|
381
|
+
# occupy either three or two characters. If the max_length can't
|
|
382
|
+
# accomodate a three-digit year, make sure we don't have one
|
|
383
|
+
definition.max_length < 7 and @year > 99
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
# Create a proper date using the given century `cc`
|
|
387
|
+
#
|
|
388
|
+
# @example
|
|
389
|
+
# DateVal.value("501015").century(19) #=> DateVal.value("19501230")
|
|
390
|
+
#
|
|
391
|
+
# @return [Proper]
|
|
392
|
+
def century(cc)
|
|
393
|
+
date = ::Date.civil(100 * cc + @year, @month, @day)
|
|
394
|
+
Proper.new(date, usage, position)
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# Create a proper date which cannot be older than the given `date`
|
|
398
|
+
# and cannot be newer than one year after the given `date`.
|
|
399
|
+
#
|
|
400
|
+
# @example
|
|
401
|
+
# DateVal.value("501015").oldest(Date.civil(1950, 10, 20)) #=> DateVal.value("20501015")
|
|
402
|
+
# DateVal.value("501015").oldest(Date.civil(1950, 10, 15)) #=> DateVal.value("19501015")
|
|
403
|
+
# DateVal.value("501015").oldest(Date.civil(1950, 10, 10)) #=> DateVal.value("19501015")
|
|
404
|
+
#
|
|
405
|
+
# @return [Proper]
|
|
406
|
+
def oldest(date)
|
|
407
|
+
cc, yy = date.year.divmod(100)
|
|
408
|
+
|
|
409
|
+
if @year < yy
|
|
410
|
+
century(cc + 1)
|
|
411
|
+
elsif @year > yy
|
|
412
|
+
century(cc)
|
|
413
|
+
else
|
|
414
|
+
if @month < date.month
|
|
415
|
+
century(cc + 1)
|
|
416
|
+
elsif @month > date.month
|
|
417
|
+
century(cc)
|
|
418
|
+
else
|
|
419
|
+
if @day < date.day
|
|
420
|
+
century(cc + 1)
|
|
421
|
+
else
|
|
422
|
+
century(cc)
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# Create a proper date which cannot be newer than the given `date`
|
|
429
|
+
# and cannot be older than one year before the given `date`.
|
|
430
|
+
#
|
|
431
|
+
# @example
|
|
432
|
+
# DateVal.value("501015").newest(Date.civil(1950, 10, 20)) #=> DateVal.value("19501015")
|
|
433
|
+
# DateVal.value("501015").newest(Date.civil(1950, 10, 15)) #=> DateVal.value("19501015")
|
|
434
|
+
# DateVal.value("501015").newest(Date.civil(1950, 10, 10)) #=> DateVal.value("18501015")
|
|
435
|
+
#
|
|
436
|
+
# @return [Proper]
|
|
437
|
+
def newest(date)
|
|
438
|
+
cc, yy = date.year.divmod(100)
|
|
439
|
+
|
|
440
|
+
if @year < yy
|
|
441
|
+
century(cc)
|
|
442
|
+
elsif @year > yy
|
|
443
|
+
century(cc - 1)
|
|
444
|
+
else
|
|
445
|
+
if @month < date.month
|
|
446
|
+
century(cc)
|
|
447
|
+
elsif @month > date.month
|
|
448
|
+
century(cc - 1)
|
|
449
|
+
else
|
|
450
|
+
if @day <= date.day
|
|
451
|
+
century(cc)
|
|
452
|
+
else
|
|
453
|
+
century(cc - 1)
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# Create a proper date which cannot be newer than the current date
|
|
460
|
+
#
|
|
461
|
+
# @return [Proper]
|
|
462
|
+
def past
|
|
463
|
+
newest(Date.today)
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
# Create a proper date which cannot be older than the current date
|
|
467
|
+
#
|
|
468
|
+
# @return [Proper]
|
|
469
|
+
def future
|
|
470
|
+
oldest(Date.today)
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
# @return [String]
|
|
474
|
+
# :nocov:
|
|
475
|
+
def inspect
|
|
476
|
+
id = definition.bind do |d|
|
|
477
|
+
"[#{"% 5s" % d.id}: #{d.name}]".bind do |s|
|
|
478
|
+
if usage.forbidden?
|
|
479
|
+
ansi.forbidden(s)
|
|
480
|
+
elsif usage.required?
|
|
481
|
+
ansi.required(s)
|
|
482
|
+
else
|
|
483
|
+
ansi.optional(s)
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
ansi.element("DT.value#{id}") + "(XX#{"%02d-%02d-%02d" % [@year, @month, @day]})"
|
|
489
|
+
end
|
|
490
|
+
# :nocov:
|
|
491
|
+
|
|
492
|
+
# @return [String]
|
|
493
|
+
def to_s
|
|
494
|
+
"XX%02d%02d%02d" % [@year, @month, @day]
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
# @return [String]
|
|
498
|
+
def to_x12(truncate = true)
|
|
499
|
+
"%02d%02d%02d" % [@year, @month, @day]
|
|
500
|
+
end
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
class << DateVal
|
|
505
|
+
# @group Constructors
|
|
506
|
+
###################################################################
|
|
507
|
+
|
|
508
|
+
# @return [DateVal::Empty]
|
|
509
|
+
def empty(usage, position)
|
|
510
|
+
self::Empty.new(usage, position)
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
# @return [DateVal]
|
|
514
|
+
def value(object, usage, position)
|
|
515
|
+
if object.is_a?(DateVal)
|
|
516
|
+
object#.copy(:usage => usage, :position => position)
|
|
517
|
+
elsif object.blank?
|
|
518
|
+
self::Empty.new(usage, position)
|
|
519
|
+
elsif object.is_a?(String) or object.is_a?(StringVal)
|
|
520
|
+
string = object.to_s
|
|
521
|
+
|
|
522
|
+
if string.length < 6
|
|
523
|
+
self::Invalid.new(object, usage, position)
|
|
524
|
+
else
|
|
525
|
+
day = string.slice(-2, 2).to_i
|
|
526
|
+
month = string.slice(-4, 2).to_i
|
|
527
|
+
year = string.slice( 0..-5)
|
|
528
|
+
|
|
529
|
+
if year.length < 4
|
|
530
|
+
self::Improper.new(year.to_i, month, day, usage, position)
|
|
531
|
+
else
|
|
532
|
+
self::Proper.new(date(year, month, day), usage, position)
|
|
533
|
+
end
|
|
534
|
+
end
|
|
535
|
+
elsif object.respond_to?(:year) and object.respond_to?(:month) and object.respond_to?(:day)
|
|
536
|
+
self::Proper.new(date(object.year, object.month, object.day), usage, position)
|
|
537
|
+
elsif object.is_a?(Array) and object.length == 3
|
|
538
|
+
if object[0] <= 99
|
|
539
|
+
self::Improper.new(*object, usage, position)
|
|
540
|
+
else
|
|
541
|
+
self::Proper.new(date(*object), usage, position)
|
|
542
|
+
end
|
|
543
|
+
else
|
|
544
|
+
self::Invalid.new(object, usage, position)
|
|
545
|
+
end
|
|
546
|
+
rescue Exceptions::InvalidElementError
|
|
547
|
+
self::Invalid.new(object, usage, position)
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
# @endgroup
|
|
551
|
+
###################################################################
|
|
552
|
+
|
|
553
|
+
private
|
|
554
|
+
|
|
555
|
+
def date(year, month, day)
|
|
556
|
+
::Date.civil(year.to_i, month.to_i, day.to_i)
|
|
557
|
+
rescue
|
|
558
|
+
raise Exceptions::InvalidElementError,
|
|
559
|
+
"invalid year(#{year}), month(#{month}), day(#{day})"
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
# Prevent direct instantiation of abstract class DateVal
|
|
564
|
+
DateVal.eigenclass.send(:protected, :new)
|
|
565
|
+
DateVal::Empty.eigenclass.send(:public, :new)
|
|
566
|
+
DateVal::Proper.eigenclass.send(:public, :new)
|
|
567
|
+
DateVal::Invalid.eigenclass.send(:public, :new)
|
|
568
|
+
DateVal::Improper.eigenclass.send(:public, :new)
|
|
569
|
+
end
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
end
|