tabulard 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|