table_structure 0.3.22 → 0.4.3

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