table_structure 0.3.21 → 0.4.2
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 +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +37 -0
- data/Gemfile.lock +7 -7
- data/README.md +84 -78
- data/lib/table_structure.rb +6 -12
- data/lib/table_structure/csv/writer.rb +4 -41
- data/lib/table_structure/iterator.rb +48 -89
- data/lib/table_structure/schema.rb +63 -76
- data/lib/table_structure/schema/class_methods.rb +2 -2
- data/lib/table_structure/schema/column_builder_factory.rb +75 -0
- data/lib/table_structure/schema/columns/attributes.rb +14 -9
- data/lib/table_structure/schema/columns/schema.rb +14 -9
- data/lib/table_structure/schema/composite_class.rb +40 -0
- data/lib/table_structure/schema/definition/columns/compiler.rb +7 -3
- data/lib/table_structure/schema/definition/columns/validator.rb +2 -6
- data/lib/table_structure/schema/dsl/column_builder.rb +29 -0
- data/lib/table_structure/schema/dsl/context_builder.rb +3 -5
- data/lib/table_structure/schema/dsl/row_builder.rb +5 -5
- data/lib/table_structure/schema/{key_converter.rb → keys_builder.rb} +2 -2
- data/lib/table_structure/schema/row_context_builder_factory.rb +30 -0
- data/lib/table_structure/table.rb +31 -56
- data/lib/table_structure/utils.rb +40 -0
- data/lib/table_structure/version.rb +1 -1
- data/lib/table_structure/writer.rb +7 -54
- metadata +8 -14
- data/lib/table_structure/schema/column_converter.rb +0 -48
- data/lib/table_structure/schema/definition/column_converter.rb +0 -31
- data/lib/table_structure/schema/definition/context_builder.rb +0 -17
- data/lib/table_structure/schema/definition/row_builder.rb +0 -25
- data/lib/table_structure/schema/dsl/column_converter.rb +0 -34
- data/lib/table_structure/schema/dsl/option.rb +0 -19
- data/lib/table_structure/schema/row_builder.rb +0 -22
- data/lib/table_structure/table/column_converter.rb +0 -46
- data/lib/table_structure/table/context_builder.rb +0 -49
- data/lib/table_structure/table/iterator.rb +0 -22
- data/lib/table_structure/table/row_builder.rb +0 -38
@@ -6,27 +6,32 @@ module TableStructure
|
|
6
6
|
class Schema
|
7
7
|
def initialize(schema)
|
8
8
|
@schema = schema
|
9
|
-
@
|
9
|
+
@header_row_generator = schema.create_header_row_generator
|
10
|
+
@data_row_generator = schema.create_data_row_generator
|
10
11
|
end
|
11
12
|
|
12
|
-
def names(
|
13
|
-
@
|
13
|
+
def names(row_context, *)
|
14
|
+
@header_row_generator.call(row_context).values
|
14
15
|
end
|
15
16
|
|
16
17
|
def keys
|
17
|
-
@
|
18
|
+
@schema.columns_keys
|
18
19
|
end
|
19
20
|
|
20
|
-
def values(row_context,
|
21
|
-
@
|
21
|
+
def values(row_context, *)
|
22
|
+
@data_row_generator.call(row_context).values
|
22
23
|
end
|
23
24
|
|
24
25
|
def size
|
25
|
-
@
|
26
|
+
@schema.columns_size
|
26
27
|
end
|
27
28
|
|
28
|
-
def
|
29
|
-
@schema.
|
29
|
+
def name_callable?
|
30
|
+
@schema.contain_name_callable?
|
31
|
+
end
|
32
|
+
|
33
|
+
def value_callable?
|
34
|
+
@schema.contain_value_callable?
|
30
35
|
end
|
31
36
|
end
|
32
37
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableStructure
|
4
|
+
module Schema
|
5
|
+
class CompositeClass
|
6
|
+
def initialize
|
7
|
+
@classes = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def compose(*classes)
|
11
|
+
@classes.concat(classes.flatten.compact)
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def column_definitions
|
16
|
+
@classes
|
17
|
+
.map(&:column_definitions)
|
18
|
+
.reduce([], &:concat)
|
19
|
+
end
|
20
|
+
|
21
|
+
def context_builders
|
22
|
+
@classes
|
23
|
+
.map(&:context_builders)
|
24
|
+
.reduce({}, &:merge!)
|
25
|
+
end
|
26
|
+
|
27
|
+
def column_builders
|
28
|
+
@classes
|
29
|
+
.map(&:column_builders)
|
30
|
+
.reduce({}, &:merge!)
|
31
|
+
end
|
32
|
+
|
33
|
+
def row_builders
|
34
|
+
@classes
|
35
|
+
.map(&:row_builders)
|
36
|
+
.reduce({}, &:merge!)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -5,10 +5,14 @@ module TableStructure
|
|
5
5
|
module Definition
|
6
6
|
module Columns
|
7
7
|
class Compiler
|
8
|
-
def initialize(
|
8
|
+
def initialize(
|
9
|
+
schema_name,
|
10
|
+
definitions,
|
11
|
+
nil_definitions_ignored: false
|
12
|
+
)
|
9
13
|
@schema_name = schema_name
|
10
14
|
@definitions = definitions
|
11
|
-
@
|
15
|
+
@nil_definitions_ignored = !!nil_definitions_ignored
|
12
16
|
end
|
13
17
|
|
14
18
|
def compile(context = nil)
|
@@ -26,7 +30,7 @@ module TableStructure
|
|
26
30
|
SchemaInstance.new(definition)
|
27
31
|
elsif Utils.schema_class?(definition)
|
28
32
|
SchemaClass.new(definition)
|
29
|
-
elsif definition.nil? && @
|
33
|
+
elsif definition.nil? && @nil_definitions_ignored
|
30
34
|
next
|
31
35
|
else
|
32
36
|
raise Error.new('Invalid definition.', @schema_name, i)
|
@@ -13,15 +13,11 @@ module TableStructure
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def validate(name:, key:, size:, **)
|
16
|
-
if key.respond_to?(:call)
|
17
|
-
raise Error.new('"key" must not be lambda.', @name, @index)
|
18
|
-
end
|
16
|
+
raise Error.new('"key" must not be lambda.', @name, @index) if key.respond_to?(:call)
|
19
17
|
if !key && name.respond_to?(:call) && !size
|
20
18
|
raise Error.new('"size" must be defined, because column size cannot be determined.', @name, @index)
|
21
19
|
end
|
22
|
-
if size && size < DEFAULT_SIZE
|
23
|
-
raise Error.new('"size" must be positive.', @name, @index)
|
24
|
-
end
|
20
|
+
raise Error.new('"size" must be positive.', @name, @index) if size && size < DEFAULT_SIZE
|
25
21
|
if key && size && [key].flatten.size < size
|
26
22
|
raise Error.new('"key" size must not be less than specified "size".', @name, @index)
|
27
23
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableStructure
|
4
|
+
module Schema
|
5
|
+
module DSL
|
6
|
+
module ColumnBuilder
|
7
|
+
def column_builder(
|
8
|
+
name,
|
9
|
+
header: true,
|
10
|
+
body: true,
|
11
|
+
&block
|
12
|
+
)
|
13
|
+
column_builders[name] =
|
14
|
+
::TableStructure::Utils::TypedProc.new(
|
15
|
+
types: { header: !!header, body: !!body }.select { |_k, v| v }.keys,
|
16
|
+
&block
|
17
|
+
)
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def column_builders
|
22
|
+
@__column_builders__ ||= {}
|
23
|
+
end
|
24
|
+
|
25
|
+
alias column_converter column_builder
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -4,11 +4,9 @@ module TableStructure
|
|
4
4
|
module Schema
|
5
5
|
module DSL
|
6
6
|
module ContextBuilder
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
callable
|
11
|
-
)
|
7
|
+
# TODO: Change definition style
|
8
|
+
def context_builder(name, &block)
|
9
|
+
context_builders[name] = block
|
12
10
|
nil
|
13
11
|
end
|
14
12
|
|
@@ -6,13 +6,13 @@ module TableStructure
|
|
6
6
|
module RowBuilder
|
7
7
|
def row_builder(
|
8
8
|
name,
|
9
|
-
|
10
|
-
|
9
|
+
enabled_row_types: %i[array hash],
|
10
|
+
&block
|
11
11
|
)
|
12
12
|
row_builders[name] =
|
13
|
-
::TableStructure::
|
14
|
-
|
15
|
-
|
13
|
+
::TableStructure::Utils::TypedProc.new(
|
14
|
+
types: enabled_row_types,
|
15
|
+
&block
|
16
16
|
)
|
17
17
|
nil
|
18
18
|
end
|
@@ -2,13 +2,13 @@
|
|
2
2
|
|
3
3
|
module TableStructure
|
4
4
|
module Schema
|
5
|
-
class
|
5
|
+
class KeysBuilder
|
6
6
|
def initialize(prefix: nil, suffix: nil)
|
7
7
|
@prefix = prefix
|
8
8
|
@suffix = suffix
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
11
|
+
def build(keys)
|
12
12
|
return keys unless has_any_options?
|
13
13
|
|
14
14
|
keys.map do |key|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableStructure
|
4
|
+
module Schema
|
5
|
+
class RowContextBuilderFactory
|
6
|
+
def initialize(schema, builders)
|
7
|
+
@schema = schema
|
8
|
+
@builders = builders
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_header_builder
|
12
|
+
return unless @schema.contain_name_callable?
|
13
|
+
return unless @builders.key?(:header)
|
14
|
+
|
15
|
+
proc do |context|
|
16
|
+
@builders[:header].call(context)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_data_builder
|
21
|
+
return unless @schema.contain_value_callable?
|
22
|
+
return unless @builders.key?(:row)
|
23
|
+
|
24
|
+
proc do |context|
|
25
|
+
@builders[:row].call(context)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -2,71 +2,46 @@
|
|
2
2
|
|
3
3
|
module TableStructure
|
4
4
|
class Table
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
apply_to_name: schema.contain_callable?(:name),
|
13
|
-
apply_to_value: schema.contain_callable?(:value),
|
14
|
-
context: schema.context
|
15
|
-
) { |mod| extend mod }
|
16
|
-
|
17
|
-
ColumnConverter.create_module(
|
18
|
-
schema.column_converters,
|
19
|
-
context: schema.context
|
20
|
-
) { |mod| extend mod }
|
5
|
+
DEFAULT_ROW_BUILDERS = {
|
6
|
+
_to_hash_: Utils::TypedProc.new(
|
7
|
+
types: :hash
|
8
|
+
) do |values, keys, *|
|
9
|
+
keys.map.with_index { |key, i| [key || i, values[i]] }.to_h
|
10
|
+
end
|
11
|
+
}.freeze
|
21
12
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
13
|
+
def initialize(schema, row_type: :array)
|
14
|
+
@header_row_generator = schema.create_header_row_generator
|
15
|
+
@data_row_generator = schema.create_data_row_generator
|
16
|
+
|
17
|
+
row_builders =
|
18
|
+
DEFAULT_ROW_BUILDERS
|
19
|
+
.merge(schema.row_builders)
|
20
|
+
.select { |_k, v| v.typed?(row_type) }
|
21
|
+
.values
|
22
|
+
|
23
|
+
unless row_builders.empty?
|
24
|
+
row_build_task = proc do |row|
|
25
|
+
row.values = row_builders.reduce(row.values) do |values, builder|
|
26
|
+
builder.call(values, row.keys, row.context, schema.context)
|
27
|
+
end
|
28
|
+
row
|
29
|
+
end
|
30
|
+
@header_row_generator.compose(row_build_task)
|
31
|
+
@data_row_generator.compose(row_build_task)
|
32
|
+
end
|
28
33
|
|
29
34
|
yield self if block_given?
|
30
35
|
end
|
31
36
|
|
32
37
|
def header(context: nil)
|
33
|
-
|
38
|
+
@header_row_generator.call(context).values
|
34
39
|
end
|
35
40
|
|
36
|
-
def body(
|
37
|
-
Enumerator.new do |y|
|
38
|
-
|
41
|
+
def body(contexts)
|
42
|
+
::Enumerator.new do |y|
|
43
|
+
contexts.each { |context| y << @data_row_generator.call(context).values }
|
39
44
|
end
|
40
45
|
end
|
41
|
-
|
42
|
-
def rows(items)
|
43
|
-
warn '[TableStructure] `TableStructure::Table#rows(items)` has been deprecated. Use `TableStructure::Table#body(items)` instead.'
|
44
|
-
body(items)
|
45
|
-
end
|
46
|
-
|
47
|
-
def row(context: nil)
|
48
|
-
warn '[TableStructure] `TableStructure::Table#row(context: ...)` has been deprecated. Use `TableStructure::Table#body(items)` instead.'
|
49
|
-
data(context: context)
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def data(context: nil)
|
55
|
-
row_values(:values, context)
|
56
|
-
end
|
57
|
-
|
58
|
-
def keys
|
59
|
-
@keys ||= @key_converter.convert(@columns.map(&:keys).flatten)
|
60
|
-
end
|
61
|
-
|
62
|
-
def size
|
63
|
-
@size ||= @columns.map(&:size).reduce(0, &:+)
|
64
|
-
end
|
65
|
-
|
66
|
-
def row_values(method, context)
|
67
|
-
@columns
|
68
|
-
.map { |column| column.send(method, context, @context) }
|
69
|
-
.flatten
|
70
|
-
end
|
71
46
|
end
|
72
47
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableStructure
|
4
|
+
module Utils
|
5
|
+
class Proc < ::Proc
|
6
|
+
attr_reader :options
|
7
|
+
|
8
|
+
def initialize(**options, &block)
|
9
|
+
@options = options
|
10
|
+
super(&block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class TypedProc < Proc
|
15
|
+
def initialize(types:, **options, &block)
|
16
|
+
@types = [types].flatten.compact
|
17
|
+
super(**options, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def typed?(type)
|
21
|
+
@types.include?(type)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class CompositeCallable
|
26
|
+
def initialize
|
27
|
+
@callables = []
|
28
|
+
end
|
29
|
+
|
30
|
+
def compose(*callables)
|
31
|
+
@callables.concat(callables.flatten.compact)
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def call(source)
|
36
|
+
@callables.reduce(source) { |memo, callable| callable.call(memo) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -4,28 +4,10 @@ module TableStructure
|
|
4
4
|
class Writer
|
5
5
|
def initialize(
|
6
6
|
schema,
|
7
|
-
header: { context: nil },
|
7
|
+
header: { context: nil, step: nil },
|
8
8
|
method: :<<,
|
9
|
-
row_type: :array
|
10
|
-
**deprecated_options
|
9
|
+
row_type: :array
|
11
10
|
)
|
12
|
-
if deprecated_options.key?(:header_omitted)
|
13
|
-
header_omitted = deprecated_options[:header_omitted]
|
14
|
-
warn "[TableStructure] `header_omitted: #{!!header_omitted}` option has been deprecated. Use `header: #{!header_omitted}` option instead."
|
15
|
-
header = !header_omitted
|
16
|
-
end
|
17
|
-
|
18
|
-
if deprecated_options.key?(:header_context)
|
19
|
-
header_context = deprecated_options[:header_context]
|
20
|
-
warn '[TableStructure] `:header_context` option has been deprecated. Use `header: { context: ... }` option instead.'
|
21
|
-
header = { context: header_context }
|
22
|
-
end
|
23
|
-
|
24
|
-
if deprecated_options.key?(:result_type)
|
25
|
-
warn '[TableStructure] `:result_type` option has been deprecated. Use `:row_type` option instead.'
|
26
|
-
row_type = deprecated_options[:result_type]
|
27
|
-
end
|
28
|
-
|
29
11
|
@schema = schema
|
30
12
|
@options = {
|
31
13
|
header: header,
|
@@ -38,51 +20,22 @@ module TableStructure
|
|
38
20
|
items,
|
39
21
|
to:,
|
40
22
|
method: @options[:method],
|
41
|
-
**deprecated_options,
|
42
23
|
&block
|
43
24
|
)
|
44
|
-
header = @options[:header]
|
45
|
-
row_type = @options[:row_type]
|
46
|
-
|
47
|
-
if deprecated_options.key?(:header)
|
48
|
-
header = deprecated_options[:header]
|
49
|
-
warn '[TableStructure] Use :header option on initialize method.'
|
50
|
-
end
|
51
|
-
|
52
|
-
if deprecated_options.key?(:header_omitted)
|
53
|
-
header_omitted = deprecated_options[:header_omitted]
|
54
|
-
warn "[TableStructure] `header_omitted: #{!!header_omitted}` option has been deprecated. Use `header: #{!header_omitted}` option instead."
|
55
|
-
header = !header_omitted
|
56
|
-
end
|
57
|
-
|
58
|
-
if deprecated_options.key?(:header_context)
|
59
|
-
header_context = deprecated_options[:header_context]
|
60
|
-
warn '[TableStructure] `:header_context` option has been deprecated. Use `header: { context: ... }` option instead.'
|
61
|
-
header = { context: header_context }
|
62
|
-
end
|
63
|
-
|
64
|
-
if deprecated_options.key?(:row_type)
|
65
|
-
row_type = deprecated_options[:row_type]
|
66
|
-
warn '[TableStructure] Use :row_type option on initialize method.'
|
67
|
-
end
|
68
|
-
|
69
|
-
if deprecated_options.key?(:result_type)
|
70
|
-
warn '[TableStructure] `:result_type` option has been deprecated. Use `:row_type` option instead.'
|
71
|
-
row_type = deprecated_options[:result_type]
|
72
|
-
end
|
73
|
-
|
74
25
|
output = Output.new(to, method: method)
|
75
26
|
|
76
27
|
Iterator
|
77
|
-
.new(
|
28
|
+
.new(
|
29
|
+
@schema,
|
30
|
+
header: @options[:header],
|
31
|
+
row_type: @options[:row_type]
|
32
|
+
)
|
78
33
|
.iterate(items, &block)
|
79
34
|
.each { |row| output.write(row) }
|
80
35
|
|
81
36
|
nil
|
82
37
|
end
|
83
38
|
|
84
|
-
private
|
85
|
-
|
86
39
|
class Output
|
87
40
|
def initialize(output, method: :<<)
|
88
41
|
@output = output
|