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
@@ -2,106 +2,65 @@
|
|
2
2
|
|
3
3
|
module TableStructure
|
4
4
|
class Iterator
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
class HeaderOptions
|
6
|
+
attr_reader :enabled, :context, :step
|
7
|
+
alias enabled? enabled
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@enabled = !!options
|
11
|
+
if options.is_a?(Hash)
|
12
|
+
@context = options[:context]
|
13
|
+
@step = options[:step]
|
14
|
+
end
|
15
|
+
|
16
|
+
validate
|
15
17
|
end
|
16
18
|
|
17
|
-
|
18
|
-
header_context = deprecated_options[:header_context]
|
19
|
-
warn '[TableStructure] `:header_context` option has been deprecated. Use `header: { context: ... }` option instead.'
|
20
|
-
header = { context: header_context }
|
21
|
-
end
|
19
|
+
private
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
def validate
|
22
|
+
if @step
|
23
|
+
raise ::TableStructure::Error, ':step must be numeric.' unless @step.is_a?(Numeric)
|
24
|
+
raise ::TableStructure::Error, ':step must be positive number.' unless @step.positive?
|
25
|
+
end
|
26
26
|
end
|
27
|
-
|
28
|
-
unless schema.is_a?(Schema)
|
29
|
-
raise ::TableStructure::Error, "Must be use Schema. #{schema}"
|
30
|
-
end
|
31
|
-
|
32
|
-
@schema = schema
|
33
|
-
@options = {
|
34
|
-
header: header,
|
35
|
-
row_type: row_type
|
36
|
-
}
|
37
27
|
end
|
38
28
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
29
|
+
def initialize(
|
30
|
+
schema,
|
31
|
+
header: { context: nil, step: nil },
|
32
|
+
row_type: :array
|
43
33
|
)
|
44
|
-
|
45
|
-
|
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
|
-
items = enumerize(items)
|
75
|
-
|
76
|
-
enum =
|
77
|
-
Table::Iterator
|
78
|
-
.new(
|
79
|
-
Table.new(@schema, row_type: row_type),
|
80
|
-
header: header
|
81
|
-
)
|
82
|
-
.iterate(items)
|
34
|
+
@table = Table.new(schema, row_type: row_type)
|
35
|
+
@header_options = HeaderOptions.new(header)
|
36
|
+
end
|
83
37
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
38
|
+
def iterate(items, &block)
|
39
|
+
raise ::TableStructure::Error, "Must be enumerable. #{items}" unless items.respond_to?(:each)
|
40
|
+
|
41
|
+
table_enum = ::Enumerator.new do |y|
|
42
|
+
body_enum = @table.body(items)
|
43
|
+
|
44
|
+
if @header_options.enabled?
|
45
|
+
header_row = @table.header(context: @header_options.context)
|
46
|
+
y << header_row
|
47
|
+
|
48
|
+
if @header_options.step
|
49
|
+
loop do
|
50
|
+
@header_options.step.times { y << body_enum.next }
|
51
|
+
y << header_row
|
52
|
+
end
|
53
|
+
else
|
54
|
+
body_enum.each { |row| y << row }
|
55
|
+
end
|
56
|
+
else
|
57
|
+
body_enum.each { |row| y << row }
|
58
|
+
end
|
89
59
|
end
|
90
60
|
|
91
|
-
|
92
|
-
end
|
93
|
-
|
94
|
-
private
|
61
|
+
table_enum = table_enum.lazy.map(&block) if block_given?
|
95
62
|
|
96
|
-
|
97
|
-
if items.respond_to?(:each)
|
98
|
-
items
|
99
|
-
elsif items.respond_to?(:call)
|
100
|
-
warn "[TableStructure] Use `Enumerator` to wrap items instead of `lambda`. The use of `lambda` has been deprecated. #{items}"
|
101
|
-
Enumerator.new { |y| items.call(y) }
|
102
|
-
else
|
103
|
-
raise ::TableStructure::Error, "Must be enumerable. #{items}"
|
104
|
-
end
|
63
|
+
table_enum
|
105
64
|
end
|
106
65
|
end
|
107
66
|
end
|
@@ -3,29 +3,25 @@
|
|
3
3
|
module TableStructure
|
4
4
|
module Schema
|
5
5
|
def self.included(klass)
|
6
|
-
klass.extend(DSL::
|
6
|
+
klass.extend(DSL::ColumnBuilder)
|
7
7
|
klass.extend(DSL::ColumnDefinition)
|
8
8
|
klass.extend(DSL::ContextBuilder)
|
9
|
-
klass.extend(DSL::Option)
|
10
9
|
klass.extend(DSL::RowBuilder)
|
11
10
|
klass.extend(ClassMethods)
|
12
11
|
end
|
13
12
|
|
14
13
|
def self.create_class(&block)
|
15
|
-
raise ::TableStructure::Error, 'No block given.' unless block
|
14
|
+
raise ::TableStructure::Error, 'No block has been given.' unless block
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
include schema_module
|
16
|
+
::Class.new do
|
17
|
+
include Schema
|
20
18
|
class_eval(&block)
|
21
19
|
end
|
22
20
|
end
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
:key_converter,
|
28
|
-
:row_builders,
|
22
|
+
Row = ::Struct.new(:keys, :values, :context)
|
23
|
+
|
24
|
+
attr_reader :row_builders,
|
29
25
|
:context
|
30
26
|
|
31
27
|
def initialize(
|
@@ -36,96 +32,87 @@ module TableStructure
|
|
36
32
|
key_prefix: nil,
|
37
33
|
key_suffix: nil,
|
38
34
|
nil_definitions_ignored: false,
|
39
|
-
**deprecated_options,
|
40
35
|
&block
|
41
36
|
)
|
42
|
-
|
43
|
-
|
44
|
-
end
|
37
|
+
schema_class = CompositeClass.new.compose(self.class)
|
38
|
+
schema_class.compose(Schema.create_class(&block)) if block
|
45
39
|
|
46
|
-
|
47
|
-
raise ::TableStructure::Error, ':result_type option has been deprecated. Use :row_type option instead.'
|
48
|
-
end
|
49
|
-
|
50
|
-
options =
|
51
|
-
[
|
52
|
-
self.class.options,
|
53
|
-
{
|
54
|
-
name_prefix: name_prefix,
|
55
|
-
name_suffix: name_suffix,
|
56
|
-
key_prefix: key_prefix,
|
57
|
-
key_suffix: key_suffix,
|
58
|
-
nil_definitions_ignored: nil_definitions_ignored
|
59
|
-
},
|
60
|
-
deprecated_options
|
61
|
-
]
|
62
|
-
.reduce({}, &:merge!)
|
63
|
-
|
64
|
-
schema_classes = [self.class]
|
65
|
-
|
66
|
-
if block_given?
|
67
|
-
schema_classes << ::TableStructure::Schema.create_class(&block)
|
68
|
-
end
|
40
|
+
context_builders = schema_class.context_builders
|
69
41
|
|
70
|
-
|
71
|
-
schema_classes
|
72
|
-
.map(&:context_builders)
|
73
|
-
.reduce({}, &:merge!)
|
74
|
-
|
75
|
-
table_context_builder = @context_builders.delete(:table)
|
42
|
+
table_context_builder = context_builders.delete(:table)
|
76
43
|
|
77
44
|
@context = table_context_builder ? table_context_builder.call(context) : context
|
78
45
|
|
79
|
-
@
|
80
|
-
schema_classes
|
81
|
-
.map(&:column_converters)
|
82
|
-
.reduce({}, &:merge!)
|
83
|
-
.merge(
|
84
|
-
ColumnConverter.create_optional_converters(
|
85
|
-
name_prefix: options.delete(:name_prefix),
|
86
|
-
name_suffix: options.delete(:name_suffix)
|
87
|
-
)
|
88
|
-
)
|
46
|
+
@row_context_builder_factory = RowContextBuilderFactory.new(self, context_builders)
|
89
47
|
|
90
|
-
@
|
91
|
-
|
92
|
-
|
48
|
+
@column_builder_factory = ColumnBuilderFactory.new(
|
49
|
+
schema_class.column_builders,
|
50
|
+
context: @context,
|
51
|
+
name_prefix: name_prefix,
|
52
|
+
name_suffix: name_suffix
|
93
53
|
)
|
94
54
|
|
95
|
-
@
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
55
|
+
@keys_builder = KeysBuilder.new(
|
56
|
+
prefix: key_prefix,
|
57
|
+
suffix: key_suffix
|
58
|
+
)
|
59
|
+
|
60
|
+
@row_builders = schema_class.row_builders
|
101
61
|
|
102
62
|
@columns =
|
103
63
|
Definition::Columns::Compiler
|
104
64
|
.new(
|
105
65
|
name,
|
106
|
-
|
107
|
-
|
66
|
+
schema_class.column_definitions,
|
67
|
+
nil_definitions_ignored: nil_definitions_ignored
|
108
68
|
)
|
109
69
|
.compile(@context)
|
70
|
+
end
|
110
71
|
|
111
|
-
|
72
|
+
def columns_keys
|
73
|
+
@columns_keys ||= @keys_builder.build(@columns.map(&:keys).flatten)
|
112
74
|
end
|
113
75
|
|
114
|
-
def
|
115
|
-
|
76
|
+
def columns_size
|
77
|
+
@columns.map(&:size).reduce(0, &:+)
|
78
|
+
end
|
116
79
|
|
117
|
-
|
80
|
+
def contain_name_callable?
|
81
|
+
@columns.any? { |column| column.name_callable? }
|
82
|
+
end
|
118
83
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
end
|
84
|
+
def contain_value_callable?
|
85
|
+
@columns.any? { |column| column.value_callable? }
|
86
|
+
end
|
123
87
|
|
124
|
-
|
88
|
+
def create_header_row_generator
|
89
|
+
::TableStructure::Utils::CompositeCallable.new.compose(
|
90
|
+
@row_context_builder_factory.create_header_builder,
|
91
|
+
proc do |context|
|
92
|
+
values =
|
93
|
+
@columns
|
94
|
+
.map { |column| column.names(context, @context) }
|
95
|
+
.flatten
|
96
|
+
|
97
|
+
Row.new(columns_keys, values, context)
|
98
|
+
end,
|
99
|
+
@column_builder_factory.create_header_builder
|
100
|
+
)
|
125
101
|
end
|
126
102
|
|
127
|
-
def
|
128
|
-
|
103
|
+
def create_data_row_generator
|
104
|
+
::TableStructure::Utils::CompositeCallable.new.compose(
|
105
|
+
@row_context_builder_factory.create_data_builder,
|
106
|
+
proc do |context|
|
107
|
+
values =
|
108
|
+
@columns
|
109
|
+
.map { |column| column.values(context, @context) }
|
110
|
+
.flatten
|
111
|
+
|
112
|
+
Row.new(columns_keys, values, context)
|
113
|
+
end,
|
114
|
+
@column_builder_factory.create_data_builder
|
115
|
+
)
|
129
116
|
end
|
130
117
|
end
|
131
118
|
end
|
@@ -36,9 +36,9 @@ module TableStructure
|
|
36
36
|
.map(&:context_builders)
|
37
37
|
.reduce({}, &:merge!)
|
38
38
|
|
39
|
-
@
|
39
|
+
@__column_builders__ =
|
40
40
|
schema_classes
|
41
|
-
.map(&:
|
41
|
+
.map(&:column_builders)
|
42
42
|
.reduce({}, &:merge!)
|
43
43
|
|
44
44
|
@__row_builders__ =
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableStructure
|
4
|
+
module Schema
|
5
|
+
class ColumnBuilderFactory
|
6
|
+
def initialize(
|
7
|
+
builders,
|
8
|
+
context: nil,
|
9
|
+
name_prefix: nil,
|
10
|
+
name_suffix: nil
|
11
|
+
)
|
12
|
+
@builders = builders
|
13
|
+
@context = context
|
14
|
+
|
15
|
+
optional_builders = {}
|
16
|
+
|
17
|
+
if name_prefix
|
18
|
+
optional_builders[:_name_prepender_] =
|
19
|
+
::TableStructure::Utils::TypedProc.new(
|
20
|
+
types: :header
|
21
|
+
) do |val, *|
|
22
|
+
val.nil? ? val : "#{name_prefix}#{val}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if name_suffix
|
27
|
+
optional_builders[:_name_appender_] =
|
28
|
+
::TableStructure::Utils::TypedProc.new(
|
29
|
+
types: :header
|
30
|
+
) do |val, *|
|
31
|
+
val.nil? ? val : "#{val}#{name_suffix}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
@builders.merge!(optional_builders) unless optional_builders.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_header_builder
|
39
|
+
builders =
|
40
|
+
@builders
|
41
|
+
.select { |_k, v| v.typed?(:header) }
|
42
|
+
.values
|
43
|
+
|
44
|
+
return if builders.empty?
|
45
|
+
|
46
|
+
proc do |row|
|
47
|
+
row.values = row.values.map do |value|
|
48
|
+
builders.reduce(value) do |value, builder|
|
49
|
+
builder.call(value, row.context, @context)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
row
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_data_builder
|
57
|
+
builders =
|
58
|
+
@builders
|
59
|
+
.select { |_k, v| v.typed?(:body) }
|
60
|
+
.values
|
61
|
+
|
62
|
+
return if builders.empty?
|
63
|
+
|
64
|
+
proc do |row|
|
65
|
+
row.values = row.values.map do |value|
|
66
|
+
builders.reduce(value) do |value, builder|
|
67
|
+
builder.call(value, row.context, @context)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
row
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -7,25 +7,30 @@ module TableStructure
|
|
7
7
|
attr_reader :keys, :size
|
8
8
|
|
9
9
|
def initialize(name:, key:, value:, size:)
|
10
|
-
@
|
10
|
+
@name_callable = Utils.callable?(name)
|
11
|
+
@name = @name_callable ? name : proc { name }
|
11
12
|
@keys = optimize_size([key].flatten, size)
|
12
|
-
@
|
13
|
+
@value_callable = Utils.callable?(value)
|
14
|
+
@value = @value_callable ? value : proc { value }
|
13
15
|
@size = size
|
14
16
|
end
|
15
17
|
|
16
18
|
def names(context, table_context)
|
17
|
-
|
18
|
-
optimize_size(
|
19
|
+
names = @name.call(context, table_context)
|
20
|
+
optimize_size(names, @size)
|
19
21
|
end
|
20
22
|
|
21
23
|
def values(context, table_context)
|
22
|
-
|
23
|
-
optimize_size(
|
24
|
+
values = @value.call(context, table_context)
|
25
|
+
optimize_size(values, @size)
|
24
26
|
end
|
25
27
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
28
|
+
def name_callable?
|
29
|
+
@name_callable
|
30
|
+
end
|
31
|
+
|
32
|
+
def value_callable?
|
33
|
+
@value_callable
|
29
34
|
end
|
30
35
|
|
31
36
|
private
|