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.
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