tabulard 0.2.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/LICENSE +202 -0
- data/README.md +43 -0
- data/VERSION +1 -0
- data/lib/sheetah/attribute.rb +60 -0
- data/lib/sheetah/attribute_types/composite.rb +57 -0
- data/lib/sheetah/attribute_types/scalar.rb +58 -0
- data/lib/sheetah/attribute_types/value.rb +62 -0
- data/lib/sheetah/attribute_types/value.rb.orig +68 -0
- data/lib/sheetah/attribute_types.rb +49 -0
- data/lib/sheetah/backends/csv.rb +92 -0
- data/lib/sheetah/backends/wrapper.rb +57 -0
- data/lib/sheetah/backends/xlsx.rb +80 -0
- data/lib/sheetah/backends.rb +11 -0
- data/lib/sheetah/column.rb +31 -0
- data/lib/sheetah/errors/error.rb +8 -0
- data/lib/sheetah/errors/spec_error.rb +10 -0
- data/lib/sheetah/errors/type_error.rb +10 -0
- data/lib/sheetah/frozen.rb +9 -0
- data/lib/sheetah/headers.rb +96 -0
- data/lib/sheetah/messaging/config.rb +19 -0
- data/lib/sheetah/messaging/constants.rb +17 -0
- data/lib/sheetah/messaging/message.rb +70 -0
- data/lib/sheetah/messaging/message_variant.rb +47 -0
- data/lib/sheetah/messaging/messages/cleaned_string.rb +18 -0
- data/lib/sheetah/messaging/messages/duplicated_header.rb +21 -0
- data/lib/sheetah/messaging/messages/invalid_header.rb +21 -0
- data/lib/sheetah/messaging/messages/missing_column.rb +21 -0
- data/lib/sheetah/messaging/messages/must_be_array.rb +18 -0
- data/lib/sheetah/messaging/messages/must_be_boolsy.rb +21 -0
- data/lib/sheetah/messaging/messages/must_be_date.rb +21 -0
- data/lib/sheetah/messaging/messages/must_be_email.rb +21 -0
- data/lib/sheetah/messaging/messages/must_be_string.rb +18 -0
- data/lib/sheetah/messaging/messages/must_exist.rb +18 -0
- data/lib/sheetah/messaging/messages/sheet_error.rb +18 -0
- data/lib/sheetah/messaging/messenger.rb +133 -0
- data/lib/sheetah/messaging/validations/base_validator.rb +43 -0
- data/lib/sheetah/messaging/validations/dsl.rb +31 -0
- data/lib/sheetah/messaging/validations/invalid_message.rb +12 -0
- data/lib/sheetah/messaging/validations/mixins.rb +57 -0
- data/lib/sheetah/messaging/validations.rb +35 -0
- data/lib/sheetah/messaging.rb +22 -0
- data/lib/sheetah/row_processor.rb +41 -0
- data/lib/sheetah/row_processor_result.rb +20 -0
- data/lib/sheetah/row_value_builder.rb +53 -0
- data/lib/sheetah/sheet/col_converter.rb +62 -0
- data/lib/sheetah/sheet.rb +107 -0
- data/lib/sheetah/sheet_processor.rb +61 -0
- data/lib/sheetah/sheet_processor_result.rb +18 -0
- data/lib/sheetah/specification.rb +30 -0
- data/lib/sheetah/template.rb +85 -0
- data/lib/sheetah/template_config.rb +35 -0
- data/lib/sheetah/types/cast.rb +20 -0
- data/lib/sheetah/types/cast_chain.rb +49 -0
- data/lib/sheetah/types/composites/array.rb +16 -0
- data/lib/sheetah/types/composites/array_compact.rb +13 -0
- data/lib/sheetah/types/composites/composite.rb +32 -0
- data/lib/sheetah/types/container.rb +81 -0
- data/lib/sheetah/types/scalars/boolsy.rb +12 -0
- data/lib/sheetah/types/scalars/boolsy_cast.rb +35 -0
- data/lib/sheetah/types/scalars/date_string.rb +12 -0
- data/lib/sheetah/types/scalars/date_string_cast.rb +43 -0
- data/lib/sheetah/types/scalars/email.rb +12 -0
- data/lib/sheetah/types/scalars/email_cast.rb +28 -0
- data/lib/sheetah/types/scalars/scalar.rb +29 -0
- data/lib/sheetah/types/scalars/scalar_cast.rb +49 -0
- data/lib/sheetah/types/scalars/string.rb +18 -0
- data/lib/sheetah/types/type.rb +103 -0
- data/lib/sheetah/utils/cell_string_cleaner.rb +29 -0
- data/lib/sheetah/utils/monadic_result.rb +174 -0
- data/lib/sheetah.rb +31 -0
- metadata +118 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "csv"
|
4
|
+
|
5
|
+
require_relative "../sheet"
|
6
|
+
|
7
|
+
module Sheetah
|
8
|
+
module Backends
|
9
|
+
class Csv
|
10
|
+
include Sheet
|
11
|
+
|
12
|
+
class InvalidCSVError < Error
|
13
|
+
end
|
14
|
+
|
15
|
+
DEFAULTS = {
|
16
|
+
row_sep: :auto,
|
17
|
+
col_sep: ",",
|
18
|
+
quote_char: '"',
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
private_constant :DEFAULTS
|
22
|
+
|
23
|
+
def self.defaults
|
24
|
+
DEFAULTS
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(
|
28
|
+
io,
|
29
|
+
row_sep: self.class.defaults[:row_sep],
|
30
|
+
col_sep: self.class.defaults[:col_sep],
|
31
|
+
quote_char: self.class.defaults[:quote_char]
|
32
|
+
)
|
33
|
+
@csv = CSV.new(
|
34
|
+
io,
|
35
|
+
row_sep: row_sep,
|
36
|
+
col_sep: col_sep,
|
37
|
+
quote_char: quote_char
|
38
|
+
)
|
39
|
+
|
40
|
+
@headers = detect_headers(@csv)
|
41
|
+
@cols_count = @headers.size
|
42
|
+
end
|
43
|
+
|
44
|
+
def each_header
|
45
|
+
return to_enum(:each_header) { @cols_count } unless block_given?
|
46
|
+
|
47
|
+
@headers.each_with_index do |header, col_idx|
|
48
|
+
col = Sheet.int2col(col_idx + 1)
|
49
|
+
|
50
|
+
yield Header.new(col: col, value: header)
|
51
|
+
end
|
52
|
+
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def each_row
|
57
|
+
return to_enum(:each_row) unless block_given?
|
58
|
+
|
59
|
+
handle_malformed_csv do
|
60
|
+
@csv.each.with_index(1) do |raw, row|
|
61
|
+
value = Array.new(@cols_count) do |col_idx|
|
62
|
+
col = Sheet.int2col(col_idx + 1)
|
63
|
+
|
64
|
+
Cell.new(row: row, col: col, value: raw[col_idx])
|
65
|
+
end
|
66
|
+
|
67
|
+
yield Row.new(row: row, value: value)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def close
|
75
|
+
# Do nothing: this backend isn't responsible for opening the IO, and therefore it is not
|
76
|
+
# responsible for closing it either.
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def handle_malformed_csv
|
82
|
+
yield
|
83
|
+
rescue CSV::MalformedCSVError
|
84
|
+
raise InvalidCSVError
|
85
|
+
end
|
86
|
+
|
87
|
+
def detect_headers(csv)
|
88
|
+
handle_malformed_csv { csv.shift } || []
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../sheet"
|
4
|
+
|
5
|
+
module Sheetah
|
6
|
+
module Backends
|
7
|
+
class Wrapper
|
8
|
+
include Sheet
|
9
|
+
|
10
|
+
def initialize(table)
|
11
|
+
raise Error if table.nil?
|
12
|
+
|
13
|
+
@table = table
|
14
|
+
|
15
|
+
if (table_size = @table.size).positive?
|
16
|
+
@headers = @table[0]
|
17
|
+
@rows_count = table_size - 1
|
18
|
+
@cols_count = @headers.size
|
19
|
+
else
|
20
|
+
@headers = []
|
21
|
+
@rows_count = 0
|
22
|
+
@cols_count = 0
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def each_header
|
27
|
+
return to_enum(:each_header) { @cols_count } unless block_given?
|
28
|
+
|
29
|
+
1.upto(@cols_count) do |col|
|
30
|
+
yield Header.new(col: Sheet.int2col(col), value: @headers[col - 1])
|
31
|
+
end
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def each_row
|
37
|
+
return to_enum(:each_row) unless block_given?
|
38
|
+
|
39
|
+
1.upto(@rows_count) do |row|
|
40
|
+
raw = @table[row]
|
41
|
+
|
42
|
+
value = Array.new(@cols_count) do |col_idx|
|
43
|
+
Cell.new(row: row, col: Sheet.int2col(col_idx + 1), value: raw[col_idx])
|
44
|
+
end
|
45
|
+
|
46
|
+
yield Row.new(row: row, value: value)
|
47
|
+
end
|
48
|
+
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def close
|
53
|
+
# nothing to do here
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# NOTE: As reference:
|
4
|
+
# - {Roo::Excelx::Cell#cell_value} => the "raw" value before Excel's typecasts
|
5
|
+
# - {Roo::Excelx::Cell#value} => the "user" value, after Excel's typecasts
|
6
|
+
require "roo"
|
7
|
+
|
8
|
+
require_relative "../sheet"
|
9
|
+
|
10
|
+
module Sheetah
|
11
|
+
module Backends
|
12
|
+
class Xlsx
|
13
|
+
include Sheet
|
14
|
+
|
15
|
+
def initialize(path)
|
16
|
+
raise Error if path.nil?
|
17
|
+
|
18
|
+
@roo = Roo::Excelx.new(path)
|
19
|
+
@is_empty = worksheet.first_row.nil?
|
20
|
+
@headers = detect_headers
|
21
|
+
@cols_count = @headers.size
|
22
|
+
end
|
23
|
+
|
24
|
+
def each_header
|
25
|
+
return to_enum(:each_header) { @cols_count } unless block_given?
|
26
|
+
|
27
|
+
@headers.each_with_index do |header, col_idx|
|
28
|
+
col = Sheet.int2col(col_idx + 1)
|
29
|
+
|
30
|
+
yield Header.new(col: col, value: header)
|
31
|
+
end
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def each_row
|
37
|
+
return to_enum(:each_row) unless block_given?
|
38
|
+
|
39
|
+
return if @is_empty
|
40
|
+
|
41
|
+
first_row = 2
|
42
|
+
last_row = worksheet.last_row
|
43
|
+
row = 0
|
44
|
+
|
45
|
+
first_row.upto(last_row) do |cursor|
|
46
|
+
raw = worksheet.row(cursor)
|
47
|
+
row += 1
|
48
|
+
|
49
|
+
value = Array.new(@cols_count) do |col_idx|
|
50
|
+
col = Sheet.int2col(col_idx + 1)
|
51
|
+
|
52
|
+
Cell.new(row: row, col: col, value: raw[col_idx])
|
53
|
+
end
|
54
|
+
|
55
|
+
yield Row.new(row: row, value: value)
|
56
|
+
end
|
57
|
+
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def close
|
62
|
+
@roo.close
|
63
|
+
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def worksheet
|
70
|
+
@worksheet ||= @roo.sheet_for(@roo.default_sheet)
|
71
|
+
end
|
72
|
+
|
73
|
+
def detect_headers
|
74
|
+
return [] if @is_empty
|
75
|
+
|
76
|
+
worksheet.row(1) || []
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sheetah
|
4
|
+
class Column
|
5
|
+
def initialize(
|
6
|
+
key:,
|
7
|
+
type:,
|
8
|
+
index:,
|
9
|
+
header:,
|
10
|
+
header_pattern:,
|
11
|
+
required:
|
12
|
+
)
|
13
|
+
@key = key
|
14
|
+
@type = type
|
15
|
+
@index = index
|
16
|
+
@header = header
|
17
|
+
@header_pattern = header_pattern
|
18
|
+
@required = required
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :key,
|
22
|
+
:type,
|
23
|
+
:index,
|
24
|
+
:header,
|
25
|
+
:header_pattern
|
26
|
+
|
27
|
+
def required?
|
28
|
+
@required
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
require_relative "messaging/messages/invalid_header"
|
5
|
+
require_relative "messaging/messages/duplicated_header"
|
6
|
+
require_relative "messaging/messages/missing_column"
|
7
|
+
|
8
|
+
module Sheetah
|
9
|
+
class Headers
|
10
|
+
include Utils::MonadicResult
|
11
|
+
|
12
|
+
class Header
|
13
|
+
def initialize(sheet_header, spec_column)
|
14
|
+
@header = sheet_header
|
15
|
+
@column = spec_column
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :header, :column
|
19
|
+
|
20
|
+
def ==(other)
|
21
|
+
other.is_a?(self.class) &&
|
22
|
+
header == other.header &&
|
23
|
+
column == other.column
|
24
|
+
end
|
25
|
+
|
26
|
+
def row_value_index
|
27
|
+
header.row_value_index
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(specification:, messenger:)
|
32
|
+
@specification = specification
|
33
|
+
@messenger = messenger
|
34
|
+
@headers = []
|
35
|
+
@columns = Set.new
|
36
|
+
@failure = false
|
37
|
+
end
|
38
|
+
|
39
|
+
def add(header)
|
40
|
+
@messenger.scope_col!(header.col) do
|
41
|
+
column = @specification.get(header.value)
|
42
|
+
|
43
|
+
return unless add_ensure_column_is_specified(header, column)
|
44
|
+
return unless add_ensure_column_is_unique(header, column)
|
45
|
+
|
46
|
+
@headers << Header.new(header, column)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def result
|
51
|
+
missing_columns = @specification.required_columns - @columns.to_a
|
52
|
+
|
53
|
+
unless missing_columns.empty?
|
54
|
+
@failure = true
|
55
|
+
|
56
|
+
missing_columns.each do |column|
|
57
|
+
@messenger.error(
|
58
|
+
Messaging::Messages::MissingColumn.new(code_data: { value: column.header })
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if @failure
|
64
|
+
Failure()
|
65
|
+
else
|
66
|
+
Success(@headers)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def add_ensure_column_is_specified(header, column)
|
73
|
+
return true unless column.nil?
|
74
|
+
|
75
|
+
unless @specification.ignore_unspecified_columns?
|
76
|
+
@failure = true
|
77
|
+
@messenger.error(
|
78
|
+
Messaging::Messages::InvalidHeader.new(code_data: { value: header.value })
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
def add_ensure_column_is_unique(header, column)
|
86
|
+
return true if @columns.add?(column)
|
87
|
+
|
88
|
+
@failure = true
|
89
|
+
@messenger.error(
|
90
|
+
Messaging::Messages::DuplicatedHeader.new(code_data: { value: header.value })
|
91
|
+
)
|
92
|
+
|
93
|
+
false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sheetah
|
4
|
+
module Messaging
|
5
|
+
class Config
|
6
|
+
def initialize(validate_messages: default_validate_messages)
|
7
|
+
@validate_messages = validate_messages
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :validate_messages
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def default_validate_messages
|
15
|
+
ENV["SHEETAH_MESSAGING_VALIDATE_MESSAGES"] != "false"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "constants"
|
4
|
+
require_relative "validations"
|
5
|
+
|
6
|
+
module Sheetah
|
7
|
+
module Messaging
|
8
|
+
class Message
|
9
|
+
include Validations
|
10
|
+
|
11
|
+
def initialize(
|
12
|
+
code:,
|
13
|
+
code_data: nil,
|
14
|
+
scope: SCOPES::SHEET,
|
15
|
+
scope_data: nil,
|
16
|
+
severity: SEVERITIES::WARN
|
17
|
+
)
|
18
|
+
@code = code
|
19
|
+
@code_data = code_data
|
20
|
+
@scope = scope
|
21
|
+
@scope_data = scope_data
|
22
|
+
@severity = severity
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor(
|
26
|
+
:code,
|
27
|
+
:code_data,
|
28
|
+
:scope,
|
29
|
+
:scope_data,
|
30
|
+
:severity
|
31
|
+
)
|
32
|
+
|
33
|
+
def ==(other)
|
34
|
+
other.is_a?(self.class) &&
|
35
|
+
code == other.code &&
|
36
|
+
code_data == other.code_data &&
|
37
|
+
scope == other.scope &&
|
38
|
+
scope_data == other.scope_data &&
|
39
|
+
severity == other.severity
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
parts = [scoping_to_s, "#{severity}: #{code}", code_data]
|
44
|
+
parts.compact!
|
45
|
+
parts.join(" ")
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_h
|
49
|
+
{
|
50
|
+
code: code,
|
51
|
+
code_data: code_data,
|
52
|
+
scope: scope,
|
53
|
+
scope_data: scope_data,
|
54
|
+
severity: severity,
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def scoping_to_s
|
61
|
+
case scope
|
62
|
+
when SCOPES::SHEET then "[#{scope}]"
|
63
|
+
when SCOPES::ROW then "[#{scope}: #{scope_data[:row]}]"
|
64
|
+
when SCOPES::COL then "[#{scope}: #{scope_data[:col]}]"
|
65
|
+
when SCOPES::CELL then "[#{scope}: #{scope_data[:col]}#{scope_data[:row]}]"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "message"
|
4
|
+
|
5
|
+
module Sheetah
|
6
|
+
module Messaging
|
7
|
+
# While a {Message} represents any kind of message, {MessageVariant} represents any subset of
|
8
|
+
# messages that share the same code.
|
9
|
+
#
|
10
|
+
# The code of a message variant can and should be defined at the class level, given that it
|
11
|
+
# won't differ among the instances (and a validation is defined to enforce that invariant).
|
12
|
+
# {MessageVariant} should be considered an abstract class, and its subclasses should define
|
13
|
+
# their own `CODE` constant, which will be read by {.code}.
|
14
|
+
#
|
15
|
+
# As far as the other methods are concerned, {.code} should be considered the only source of
|
16
|
+
# truth when it comes to reading the code assigned to a message variant. The fact that {.code}
|
17
|
+
# is actually implemented using a dynamic resolution of the class' `CODE` constant is an
|
18
|
+
# implementation detail stemming from the fact that documentation tools such as YARD will
|
19
|
+
# highlight constants, as opposed to instance variables of a class for example. Using a constant
|
20
|
+
# is therefore meant to provide better documentation, and it should not be relied upon
|
21
|
+
# otherwise.
|
22
|
+
#
|
23
|
+
# @abstract
|
24
|
+
class MessageVariant < Message
|
25
|
+
# Reads the code assigned to the class (and its instances)
|
26
|
+
# @return [String]
|
27
|
+
def self.code
|
28
|
+
self::CODE
|
29
|
+
end
|
30
|
+
|
31
|
+
# Simplifies the initialization of a variant
|
32
|
+
#
|
33
|
+
# Contrary to the requirements of {Message#initialize}, {MessageVariant.new} doesn't require
|
34
|
+
# the caller to pass the `:code` keyword argument, as it is capable of prodividing it
|
35
|
+
# automatically (from reading {.code}).
|
36
|
+
def self.new(**opts)
|
37
|
+
super(code: code, **opts)
|
38
|
+
end
|
39
|
+
|
40
|
+
def_validator do
|
41
|
+
def validate_code(message)
|
42
|
+
message.code == message.class.code
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../message_variant"
|
4
|
+
|
5
|
+
module Sheetah
|
6
|
+
module Messaging
|
7
|
+
module Messages
|
8
|
+
class CleanedString < MessageVariant
|
9
|
+
CODE = "cleaned_string"
|
10
|
+
|
11
|
+
def_validator do
|
12
|
+
cell
|
13
|
+
nil_code_data
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../message_variant"
|
4
|
+
|
5
|
+
module Sheetah
|
6
|
+
module Messaging
|
7
|
+
module Messages
|
8
|
+
class DuplicatedHeader < MessageVariant
|
9
|
+
CODE = "duplicated_header"
|
10
|
+
|
11
|
+
def_validator do
|
12
|
+
col
|
13
|
+
|
14
|
+
def validate_code_data(message)
|
15
|
+
message.code_data in { value: String }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../message_variant"
|
4
|
+
|
5
|
+
module Sheetah
|
6
|
+
module Messaging
|
7
|
+
module Messages
|
8
|
+
class InvalidHeader < MessageVariant
|
9
|
+
CODE = "invalid_header"
|
10
|
+
|
11
|
+
def_validator do
|
12
|
+
col
|
13
|
+
|
14
|
+
def validate_code_data(message)
|
15
|
+
message.code_data in { value: String }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../message_variant"
|
4
|
+
|
5
|
+
module Sheetah
|
6
|
+
module Messaging
|
7
|
+
module Messages
|
8
|
+
class MissingColumn < MessageVariant
|
9
|
+
CODE = "missing_column"
|
10
|
+
|
11
|
+
def_validator do
|
12
|
+
sheet
|
13
|
+
|
14
|
+
def validate_code_data(message)
|
15
|
+
message.code_data in { value: String }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../message_variant"
|
4
|
+
|
5
|
+
module Sheetah
|
6
|
+
module Messaging
|
7
|
+
module Messages
|
8
|
+
class MustBeArray < MessageVariant
|
9
|
+
CODE = "must_be_array"
|
10
|
+
|
11
|
+
def_validator do
|
12
|
+
cell
|
13
|
+
nil_code_data
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../message_variant"
|
4
|
+
|
5
|
+
module Sheetah
|
6
|
+
module Messaging
|
7
|
+
module Messages
|
8
|
+
class MustBeBoolsy < MessageVariant
|
9
|
+
CODE = "must_be_boolsy"
|
10
|
+
|
11
|
+
def_validator do
|
12
|
+
cell
|
13
|
+
|
14
|
+
def validate_code_data(message)
|
15
|
+
message.code_data in { value: String }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|