table_structure 0.3.22 → 0.4.3

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +33 -0
  4. data/Gemfile.lock +7 -7
  5. data/README.md +30 -31
  6. data/lib/table_structure.rb +6 -12
  7. data/lib/table_structure/csv/writer.rb +4 -41
  8. data/lib/table_structure/iterator.rb +48 -89
  9. data/lib/table_structure/schema.rb +63 -76
  10. data/lib/table_structure/schema/class_methods.rb +9 -28
  11. data/lib/table_structure/schema/column_builder_factory.rb +75 -0
  12. data/lib/table_structure/schema/columns/attributes.rb +14 -9
  13. data/lib/table_structure/schema/columns/schema.rb +14 -9
  14. data/lib/table_structure/schema/composite_class.rb +40 -0
  15. data/lib/table_structure/schema/definition/columns/compiler.rb +7 -3
  16. data/lib/table_structure/schema/definition/columns/validator.rb +2 -6
  17. data/lib/table_structure/schema/dsl/column_builder.rb +29 -0
  18. data/lib/table_structure/schema/dsl/column_definition.rb +12 -2
  19. data/lib/table_structure/schema/dsl/context_builder.rb +2 -11
  20. data/lib/table_structure/schema/dsl/row_builder.rb +2 -9
  21. data/lib/table_structure/schema/{key_converter.rb → keys_builder.rb} +2 -2
  22. data/lib/table_structure/schema/row_context_builder_factory.rb +26 -0
  23. data/lib/table_structure/table.rb +31 -56
  24. data/lib/table_structure/utils.rb +40 -0
  25. data/lib/table_structure/version.rb +1 -1
  26. data/lib/table_structure/writer.rb +7 -54
  27. metadata +8 -14
  28. data/lib/table_structure/schema/column_converter.rb +0 -46
  29. data/lib/table_structure/schema/definition/column_converter.rb +0 -31
  30. data/lib/table_structure/schema/definition/context_builder.rb +0 -17
  31. data/lib/table_structure/schema/definition/row_builder.rb +0 -25
  32. data/lib/table_structure/schema/dsl/column_converter.rb +0 -41
  33. data/lib/table_structure/schema/dsl/option.rb +0 -19
  34. data/lib/table_structure/schema/row_builder.rb +0 -21
  35. data/lib/table_structure/table/column_converter.rb +0 -46
  36. data/lib/table_structure/table/context_builder.rb +0 -49
  37. data/lib/table_structure/table/iterator.rb +0 -22
  38. data/lib/table_structure/table/row_builder.rb +0 -38
@@ -3,29 +3,25 @@
3
3
  module TableStructure
4
4
  module Schema
5
5
  def self.included(klass)
6
- klass.extend(DSL::ColumnConverter)
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
- schema_module = self
18
- Class.new do
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
- attr_reader :columns,
25
- :context_builders,
26
- :column_converters,
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
- if deprecated_options.key?(:row_type)
43
- raise ::TableStructure::Error, 'Use :row_type option with Table, Writer or Iterator.'
44
- end
37
+ schema_class = CompositeClass.new.compose(self.class)
38
+ schema_class.compose(Schema.create_class(&block)) if block
45
39
 
46
- if deprecated_options.key?(:result_type)
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
- @context_builders =
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
- @column_converters =
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
- @key_converter = KeyConverter.new(
91
- prefix: options.delete(:key_prefix),
92
- suffix: options.delete(:key_suffix)
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
- @row_builders =
96
- RowBuilder.prepend_default_builders(
97
- schema_classes
98
- .map(&:row_builders)
99
- .reduce({}, &:merge!)
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
- schema_classes.map(&:column_definitions).reduce([], &:concat),
107
- { nil_definitions_ignored: options.delete(:nil_definitions_ignored) }
66
+ schema_class.column_definitions,
67
+ nil_definitions_ignored: nil_definitions_ignored
108
68
  )
109
69
  .compile(@context)
70
+ end
110
71
 
111
- @options = options
72
+ def columns_keys
73
+ @columns_keys ||= @keys_builder.build(@columns.map(&:keys).flatten)
112
74
  end
113
75
 
114
- def create_table(row_type: :array, **deprecated_options, &block)
115
- warn '[TableStructure] `TableStructure::Schema#create_table` has been deprecated. Use `TableStructure::Table.new` instead.'
76
+ def columns_size
77
+ @columns.map(&:size).reduce(0, &:+)
78
+ end
116
79
 
117
- options = @options.merge(deprecated_options)
80
+ def contain_name_callable?
81
+ @columns.any? { |column| column.name_callable? }
82
+ end
118
83
 
119
- if options.key?(:result_type)
120
- warn '[TableStructure] `:result_type` option has been deprecated. Use `:row_type` option instead.'
121
- options[:row_type] = options[:result_type]
122
- end
84
+ def contain_value_callable?
85
+ @columns.any? { |column| column.value_callable? }
86
+ end
123
87
 
124
- ::TableStructure::Table.new(self, row_type: options[:row_type] || row_type, &block)
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 contain_callable?(attribute)
128
- @columns.any? { |column| column.contain_callable?(attribute) }
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
@@ -4,13 +4,11 @@ module TableStructure
4
4
  module Schema
5
5
  module ClassMethods
6
6
  def +(other)
7
- unless ::TableStructure::Schema::Utils.schema_class?(other)
8
- raise ::TableStructure::Error, "Must be a schema class. #{other}"
9
- end
7
+ raise ::TableStructure::Error, "Must be a schema class. [#{other}]" unless Utils.schema_class?(other)
10
8
 
11
9
  self_class = self
12
10
 
13
- ::TableStructure::Schema.create_class do
11
+ Schema.create_class do
14
12
  columns self_class
15
13
  columns other
16
14
  end
@@ -18,33 +16,16 @@ module TableStructure
18
16
 
19
17
  def merge(*others)
20
18
  others.each do |other|
21
- unless ::TableStructure::Schema::Utils.schema_class?(other)
22
- raise ::TableStructure::Error, "Must be a schema class. #{other}"
23
- end
19
+ raise ::TableStructure::Error, "Must be a schema class. [#{other}]" unless Utils.schema_class?(other)
24
20
  end
25
21
 
26
- schema_classes = [self, *others]
27
-
28
- ::TableStructure::Schema.create_class do
29
- @__column_definitions__ =
30
- schema_classes
31
- .map(&:column_definitions)
32
- .flatten
33
-
34
- @__context_builders__ =
35
- schema_classes
36
- .map(&:context_builders)
37
- .reduce({}, &:merge!)
38
-
39
- @__column_converters__ =
40
- schema_classes
41
- .map(&:column_converters)
42
- .reduce({}, &:merge!)
22
+ schema_class = CompositeClass.new.compose(self, *others)
43
23
 
44
- @__row_builders__ =
45
- schema_classes
46
- .map(&:row_builders)
47
- .reduce({}, &:merge!)
24
+ Schema.create_class do
25
+ @__column_definitions__ = schema_class.column_definitions
26
+ @__context_builders__ = schema_class.context_builders
27
+ @__column_builders__ = schema_class.column_builders
28
+ @__row_builders__ = schema_class.row_builders
48
29
  end
49
30
  end
50
31
  end
@@ -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
- @name = name
10
+ @name_callable = Utils.callable?(name)
11
+ @name = @name_callable ? name : proc { name }
11
12
  @keys = optimize_size([key].flatten, size)
12
- @value = value
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
- name = Utils.evaluate_callable(@name, context, table_context)
18
- optimize_size(name, @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
- value = Utils.evaluate_callable(@value, context, table_context)
23
- optimize_size(value, @size)
24
+ values = @value.call(context, table_context)
25
+ optimize_size(values, @size)
24
26
  end
25
27
 
26
- def contain_callable?(attribute)
27
- val = instance_variable_get("@#{attribute}")
28
- Utils.callable?(val)
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
@@ -6,27 +6,32 @@ module TableStructure
6
6
  class Schema
7
7
  def initialize(schema)
8
8
  @schema = schema
9
- @table = ::TableStructure::Table.new(schema)
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(header_context, _table_context)
13
- @table.header(context: header_context)
13
+ def names(row_context, *)
14
+ @header_row_generator.call(row_context).values
14
15
  end
15
16
 
16
17
  def keys
17
- @table.send(:keys)
18
+ @schema.columns_keys
18
19
  end
19
20
 
20
- def values(row_context, _table_context)
21
- @table.send(:data, context: row_context)
21
+ def values(row_context, *)
22
+ @data_row_generator.call(row_context).values
22
23
  end
23
24
 
24
25
  def size
25
- @table.send(:size)
26
+ @schema.columns_size
26
27
  end
27
28
 
28
- def contain_callable?(attribute)
29
- @schema.contain_callable?(attribute)
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
+ .flatten
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(schema_name, definitions, options)
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
- @options = options
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? && @options[:nil_definitions_ignored]
33
+ elsif definition.nil? && @nil_definitions_ignored
30
34
  next
31
35
  else
32
36
  raise Error.new('Invalid definition.', @schema_name, i)