table_structure 0.3.21 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|