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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +37 -0
  4. data/Gemfile.lock +7 -7
  5. data/README.md +84 -78
  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 +2 -2
  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/context_builder.rb +3 -5
  19. data/lib/table_structure/schema/dsl/row_builder.rb +5 -5
  20. data/lib/table_structure/schema/{key_converter.rb → keys_builder.rb} +2 -2
  21. data/lib/table_structure/schema/row_context_builder_factory.rb +30 -0
  22. data/lib/table_structure/table.rb +31 -56
  23. data/lib/table_structure/utils.rb +40 -0
  24. data/lib/table_structure/version.rb +1 -1
  25. data/lib/table_structure/writer.rb +7 -54
  26. metadata +8 -14
  27. data/lib/table_structure/schema/column_converter.rb +0 -48
  28. data/lib/table_structure/schema/definition/column_converter.rb +0 -31
  29. data/lib/table_structure/schema/definition/context_builder.rb +0 -17
  30. data/lib/table_structure/schema/definition/row_builder.rb +0 -25
  31. data/lib/table_structure/schema/dsl/column_converter.rb +0 -34
  32. data/lib/table_structure/schema/dsl/option.rb +0 -19
  33. data/lib/table_structure/schema/row_builder.rb +0 -22
  34. data/lib/table_structure/table/column_converter.rb +0 -46
  35. data/lib/table_structure/table/context_builder.rb +0 -49
  36. data/lib/table_structure/table/iterator.rb +0 -22
  37. 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
- @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
+ .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(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)
@@ -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
- def context_builder(name, callable)
8
- context_builders[name] =
9
- ::TableStructure::Schema::Definition::ContextBuilder.new(
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
- callable,
10
- enabled_row_types: %i[array hash]
9
+ enabled_row_types: %i[array hash],
10
+ &block
11
11
  )
12
12
  row_builders[name] =
13
- ::TableStructure::Schema::Definition::RowBuilder.new(
14
- callable,
15
- enabled_row_types: enabled_row_types
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 KeyConverter
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 convert(keys)
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
- def initialize(schema, row_type: :array)
6
- @columns = schema.columns
7
- @context = schema.context
8
- @key_converter = schema.key_converter
9
-
10
- ContextBuilder.create_module(
11
- schema.context_builders,
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
- RowBuilder.create_module(
23
- schema.row_builders,
24
- row_type: row_type,
25
- keys: keys,
26
- context: schema.context
27
- ) { |mod| extend mod }
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
- row_values(:names, context)
38
+ @header_row_generator.call(context).values
34
39
  end
35
40
 
36
- def body(items)
37
- Enumerator.new do |y|
38
- items.each { |item| y << data(context: item) }
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TableStructure
4
- VERSION = '0.3.21'
4
+ VERSION = '0.4.2'
5
5
  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(@schema, header: header, row_type: row_type)
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