table_structure 0.4.1 → 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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +10 -0
  4. data/Gemfile.lock +7 -7
  5. data/README.md +3 -3
  6. data/lib/table_structure.rb +6 -10
  7. data/lib/table_structure/iterator.rb +13 -4
  8. data/lib/table_structure/schema.rb +67 -41
  9. data/lib/table_structure/schema/class_methods.rb +2 -2
  10. data/lib/table_structure/schema/column_builder_factory.rb +75 -0
  11. data/lib/table_structure/schema/columns/attributes.rb +14 -9
  12. data/lib/table_structure/schema/columns/schema.rb +14 -9
  13. data/lib/table_structure/schema/composite_class.rb +40 -0
  14. data/lib/table_structure/schema/definition/columns/compiler.rb +7 -3
  15. data/lib/table_structure/schema/definition/columns/validator.rb +2 -6
  16. data/lib/table_structure/schema/dsl/column_builder.rb +29 -0
  17. data/lib/table_structure/schema/dsl/context_builder.rb +1 -4
  18. data/lib/table_structure/schema/dsl/row_builder.rb +2 -2
  19. data/lib/table_structure/schema/{key_converter.rb → keys_builder.rb} +2 -2
  20. data/lib/table_structure/schema/row_context_builder_factory.rb +30 -0
  21. data/lib/table_structure/table.rb +31 -46
  22. data/lib/table_structure/utils.rb +40 -0
  23. data/lib/table_structure/version.rb +1 -1
  24. data/lib/table_structure/writer.rb +0 -2
  25. metadata +8 -12
  26. data/lib/table_structure/schema/column_converter.rb +0 -46
  27. data/lib/table_structure/schema/definition/column_converter.rb +0 -31
  28. data/lib/table_structure/schema/definition/context_builder.rb +0 -17
  29. data/lib/table_structure/schema/definition/row_builder.rb +0 -25
  30. data/lib/table_structure/schema/dsl/column_converter.rb +0 -28
  31. data/lib/table_structure/schema/row_builder.rb +0 -21
  32. data/lib/table_structure/table/column_converter.rb +0 -46
  33. data/lib/table_structure/table/context_builder.rb +0 -49
  34. data/lib/table_structure/table/row_builder.rb +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8bffa1aa49a812a2f4c1998aac9a297eeabe871a0c49376da090546da3a47a2d
4
- data.tar.gz: 8bd2243648e9c2768339813f407e81cf2a94bd5e858349f9de0eaeb52acb6bfc
3
+ metadata.gz: 06f36abaa1bc4478901ea51a27e0e22b00ecf70ecbfd18b4c785c2e86e08ca1d
4
+ data.tar.gz: 26990e2f8ac30764a3d571dbf8a35e422ce163989f9eda5e92aa3c2b9e5cf498
5
5
  SHA512:
6
- metadata.gz: 523e57f201c4aad11b038156676fa3c0c8835248366a7bb49929dc580265001f73c4a858049e972a782bf99d7f0a664407a6e258bdf94c616225b633c5d3461c
7
- data.tar.gz: e3620fdee15c2bdb77d22224426d31c83349ba469f386681926d58963d5ee986b50b98b527cbc7d23e74edd8fba0891d8db2dd9ec7e256e7421cfdae890bf224
6
+ metadata.gz: 288ea6944e5efa303a3c39e4502dce7b0d2994504796a7de0b69b28d8b0cd91f878670db9846a51cbb57d16797bb8124e50f2162db00ec89b49d83de3a345030
7
+ data.tar.gz: 72d983aef1de40282befe38d230806b1fb3deec4ecf08801822867d43ea656a6a307ad53c46ae87f2661bcc90ef5c6f0b3d8c5ea515eb80ddeb78e559b552585
@@ -8,4 +8,4 @@ rvm:
8
8
  - 2.5
9
9
  - 2.4
10
10
  - 2.3
11
- before_install: gem install bundler:2.1.2
11
+ before_install: gem install bundler:2.1.4
@@ -1,3 +1,13 @@
1
+ # 0.4.2
2
+ Changes:
3
+ - `TableStructure::Schema`
4
+ - Improve performance.
5
+ - `TableStructure::Iterator`
6
+ - `TableStructure::Writer`
7
+ - `TableStructure::CSV::Writer`
8
+ - Add validation for `header: { step: n }` option.
9
+ - `n` is allowed positive number or `nil`(default).
10
+
1
11
  # 0.4.1
2
12
  Changes:
3
13
  - `TableStructure::Iterator`
@@ -1,26 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- table_structure (0.4.1)
4
+ table_structure (0.4.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- diff-lcs (1.3)
9
+ diff-lcs (1.4.4)
10
10
  rake (13.0.1)
11
11
  rspec (3.9.0)
12
12
  rspec-core (~> 3.9.0)
13
13
  rspec-expectations (~> 3.9.0)
14
14
  rspec-mocks (~> 3.9.0)
15
- rspec-core (3.9.1)
16
- rspec-support (~> 3.9.1)
17
- rspec-expectations (3.9.1)
15
+ rspec-core (3.9.2)
16
+ rspec-support (~> 3.9.3)
17
+ rspec-expectations (3.9.2)
18
18
  diff-lcs (>= 1.2.0, < 2.0)
19
19
  rspec-support (~> 3.9.0)
20
20
  rspec-mocks (3.9.1)
21
21
  diff-lcs (>= 1.2.0, < 2.0)
22
22
  rspec-support (~> 3.9.0)
23
- rspec-support (3.9.2)
23
+ rspec-support (3.9.3)
24
24
 
25
25
  PLATFORMS
26
26
  ruby
@@ -32,4 +32,4 @@ DEPENDENCIES
32
32
  table_structure!
33
33
 
34
34
  BUNDLED WITH
35
- 2.1.2
35
+ 2.1.4
data/README.md CHANGED
@@ -106,10 +106,10 @@ items = [
106
106
  ## or
107
107
  # items = Enumerator.new { |y| Item.find_each { |item| y << item } }
108
108
 
109
- table = []
110
- writer.write(items, to: table)
109
+ array = []
110
+ writer.write(items, to: array)
111
111
 
112
- # table
112
+ # array
113
113
  # => [["ID", "Name", "Pet 1", "Pet 2", "Pet 3", "Q1", "Q2", "Q3"], ["1", "Taro", "🐱", "🐶", "", "⭕️", "❌", "⭕️"], ["2", "Hanako", "🐇", "🐢", "🐿", "⭕️", "⭕️", "❌"]]
114
114
  ```
115
115
 
@@ -5,31 +5,27 @@ module TableStructure
5
5
 
6
6
  require 'table_structure/version'
7
7
  require 'forwardable'
8
+ require 'table_structure/utils'
8
9
  require 'table_structure/schema'
9
10
  require 'table_structure/schema/class_methods'
10
- require 'table_structure/schema/dsl/column_converter'
11
+ require 'table_structure/schema/composite_class'
12
+ require 'table_structure/schema/dsl/column_builder'
11
13
  require 'table_structure/schema/dsl/column_definition'
12
14
  require 'table_structure/schema/dsl/context_builder'
13
15
  require 'table_structure/schema/dsl/row_builder'
14
- require 'table_structure/schema/definition/column_converter'
15
- require 'table_structure/schema/definition/context_builder'
16
- require 'table_structure/schema/definition/row_builder'
17
16
  require 'table_structure/schema/definition/columns/compiler'
18
17
  require 'table_structure/schema/definition/columns/error'
19
18
  require 'table_structure/schema/definition/columns/validator'
20
19
  require 'table_structure/schema/definition/columns/attributes'
21
20
  require 'table_structure/schema/definition/columns/schema_class'
22
21
  require 'table_structure/schema/definition/columns/schema_instance'
23
- require 'table_structure/schema/column_converter'
24
- require 'table_structure/schema/row_builder'
25
- require 'table_structure/schema/key_converter'
22
+ require 'table_structure/schema/column_builder_factory'
23
+ require 'table_structure/schema/keys_builder'
24
+ require 'table_structure/schema/row_context_builder_factory'
26
25
  require 'table_structure/schema/columns/attributes'
27
26
  require 'table_structure/schema/columns/schema'
28
27
  require 'table_structure/schema/utils'
29
28
  require 'table_structure/table'
30
- require 'table_structure/table/column_converter'
31
- require 'table_structure/table/context_builder'
32
- require 'table_structure/table/row_builder'
33
29
  require 'table_structure/writer'
34
30
  require 'table_structure/csv/writer'
35
31
  require 'table_structure/iterator'
@@ -12,6 +12,17 @@ module TableStructure
12
12
  @context = options[:context]
13
13
  @step = options[:step]
14
14
  end
15
+
16
+ validate
17
+ end
18
+
19
+ private
20
+
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
15
26
  end
16
27
  end
17
28
 
@@ -25,9 +36,7 @@ module TableStructure
25
36
  end
26
37
 
27
38
  def iterate(items, &block)
28
- unless items.respond_to?(:each)
29
- raise ::TableStructure::Error, "Must be enumerable. #{items}"
30
- end
39
+ raise ::TableStructure::Error, "Must be enumerable. #{items}" unless items.respond_to?(:each)
31
40
 
32
41
  table_enum = ::Enumerator.new do |y|
33
42
  body_enum = @table.body(items)
@@ -36,7 +45,7 @@ module TableStructure
36
45
  header_row = @table.header(context: @header_options.context)
37
46
  y << header_row
38
47
 
39
- if @header_options.step&.positive?
48
+ if @header_options.step
40
49
  loop do
41
50
  @header_options.step.times { y << body_enum.next }
42
51
  y << header_row
@@ -3,7 +3,7 @@
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
9
  klass.extend(DSL::RowBuilder)
@@ -11,20 +11,17 @@ module TableStructure
11
11
  end
12
12
 
13
13
  def self.create_class(&block)
14
- raise ::TableStructure::Error, 'No block given.' unless block
14
+ raise ::TableStructure::Error, 'No block has been given.' unless block
15
15
 
16
- schema_module = self
17
- Class.new do
18
- include schema_module
16
+ ::Class.new do
17
+ include Schema
19
18
  class_eval(&block)
20
19
  end
21
20
  end
22
21
 
23
- attr_reader :columns,
24
- :context_builders,
25
- :column_converters,
26
- :key_converter,
27
- :row_builders,
22
+ Row = ::Struct.new(:keys, :values, :context)
23
+
24
+ attr_reader :row_builders,
28
25
  :context
29
26
 
30
27
  def initialize(
@@ -37,56 +34,85 @@ module TableStructure
37
34
  nil_definitions_ignored: false,
38
35
  &block
39
36
  )
40
- schema_classes = [self.class]
41
-
42
- if block_given?
43
- schema_classes << ::TableStructure::Schema.create_class(&block)
44
- end
37
+ schema_class = CompositeClass.new.compose(self.class)
38
+ schema_class.compose(Schema.create_class(&block)) if block
45
39
 
46
- @context_builders =
47
- schema_classes
48
- .map(&:context_builders)
49
- .reduce({}, &:merge!)
40
+ context_builders = schema_class.context_builders
50
41
 
51
- table_context_builder = @context_builders.delete(:table)
42
+ table_context_builder = context_builders.delete(:table)
52
43
 
53
44
  @context = table_context_builder ? table_context_builder.call(context) : context
54
45
 
55
- @column_converters =
56
- schema_classes
57
- .map(&:column_converters)
58
- .reduce({}, &:merge!)
59
- .merge(
60
- ColumnConverter.create_optional_converters(
61
- name_prefix: name_prefix,
62
- name_suffix: name_suffix
63
- )
64
- )
46
+ @row_context_builder_factory = RowContextBuilderFactory.new(self, context_builders)
47
+
48
+ @column_builder_factory = ColumnBuilderFactory.new(
49
+ schema_class.column_builders,
50
+ context: @context,
51
+ name_prefix: name_prefix,
52
+ name_suffix: name_suffix
53
+ )
65
54
 
66
- @key_converter = KeyConverter.new(
55
+ @keys_builder = KeysBuilder.new(
67
56
  prefix: key_prefix,
68
57
  suffix: key_suffix
69
58
  )
70
59
 
71
- @row_builders =
72
- RowBuilder.prepend_default_builders(
73
- schema_classes
74
- .map(&:row_builders)
75
- .reduce({}, &:merge!)
76
- )
60
+ @row_builders = schema_class.row_builders
77
61
 
78
62
  @columns =
79
63
  Definition::Columns::Compiler
80
64
  .new(
81
65
  name,
82
- schema_classes.map(&:column_definitions).reduce([], &:concat),
83
- { nil_definitions_ignored: nil_definitions_ignored }
66
+ schema_class.column_definitions,
67
+ nil_definitions_ignored: nil_definitions_ignored
84
68
  )
85
69
  .compile(@context)
86
70
  end
87
71
 
88
- def contain_callable?(attribute)
89
- @columns.any? { |column| column.contain_callable?(attribute) }
72
+ def columns_keys
73
+ @columns_keys ||= @keys_builder.build(@columns.map(&:keys).flatten)
74
+ end
75
+
76
+ def columns_size
77
+ @columns.map(&:size).reduce(0, &:+)
78
+ end
79
+
80
+ def contain_name_callable?
81
+ @columns.any? { |column| column.name_callable? }
82
+ end
83
+
84
+ def contain_value_callable?
85
+ @columns.any? { |column| column.value_callable? }
86
+ end
87
+
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
+ )
101
+ end
102
+
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
+ )
90
116
  end
91
117
  end
92
118
  end
@@ -36,9 +36,9 @@ module TableStructure
36
36
  .map(&:context_builders)
37
37
  .reduce({}, &:merge!)
38
38
 
39
- @__column_converters__ =
39
+ @__column_builders__ =
40
40
  schema_classes
41
- .map(&:column_converters)
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
- @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
+ .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
@@ -6,10 +6,7 @@ module TableStructure
6
6
  module ContextBuilder
7
7
  # TODO: Change definition style
8
8
  def context_builder(name, &block)
9
- context_builders[name] =
10
- ::TableStructure::Schema::Definition::ContextBuilder.new(
11
- &block
12
- )
9
+ context_builders[name] = block
13
10
  nil
14
11
  end
15
12
 
@@ -10,8 +10,8 @@ module TableStructure
10
10
  &block
11
11
  )
12
12
  row_builders[name] =
13
- ::TableStructure::Schema::Definition::RowBuilder.new(
14
- enabled_row_types: enabled_row_types,
13
+ ::TableStructure::Utils::TypedProc.new(
14
+ types: enabled_row_types,
15
15
  &block
16
16
  )
17
17
  nil
@@ -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,61 +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
- private
43
-
44
- def data(context: nil)
45
- row_values(:values, context)
46
- end
47
-
48
- def keys
49
- @keys ||= @key_converter.convert(@columns.map(&:keys).flatten)
50
- end
51
-
52
- def size
53
- @size ||= @columns.map(&:size).reduce(0, &:+)
54
- end
55
-
56
- def row_values(method, context)
57
- @columns
58
- .map { |column| column.send(method, context, @context) }
59
- .flatten
60
- end
61
46
  end
62
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.4.1'
4
+ VERSION = '0.4.2'
5
5
  end
@@ -36,8 +36,6 @@ module TableStructure
36
36
  nil
37
37
  end
38
38
 
39
- private
40
-
41
39
  class Output
42
40
  def initialize(output, method: :<<)
43
41
  @output = output
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: table_structure
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - jsmmr
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-12 00:00:00.000000000 Z
11
+ date: 2020-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,29 +75,25 @@ files:
75
75
  - lib/table_structure/iterator.rb
76
76
  - lib/table_structure/schema.rb
77
77
  - lib/table_structure/schema/class_methods.rb
78
- - lib/table_structure/schema/column_converter.rb
78
+ - lib/table_structure/schema/column_builder_factory.rb
79
79
  - lib/table_structure/schema/columns/attributes.rb
80
80
  - lib/table_structure/schema/columns/schema.rb
81
- - lib/table_structure/schema/definition/column_converter.rb
81
+ - lib/table_structure/schema/composite_class.rb
82
82
  - lib/table_structure/schema/definition/columns/attributes.rb
83
83
  - lib/table_structure/schema/definition/columns/compiler.rb
84
84
  - lib/table_structure/schema/definition/columns/error.rb
85
85
  - lib/table_structure/schema/definition/columns/schema_class.rb
86
86
  - lib/table_structure/schema/definition/columns/schema_instance.rb
87
87
  - lib/table_structure/schema/definition/columns/validator.rb
88
- - lib/table_structure/schema/definition/context_builder.rb
89
- - lib/table_structure/schema/definition/row_builder.rb
90
- - lib/table_structure/schema/dsl/column_converter.rb
88
+ - lib/table_structure/schema/dsl/column_builder.rb
91
89
  - lib/table_structure/schema/dsl/column_definition.rb
92
90
  - lib/table_structure/schema/dsl/context_builder.rb
93
91
  - lib/table_structure/schema/dsl/row_builder.rb
94
- - lib/table_structure/schema/key_converter.rb
95
- - lib/table_structure/schema/row_builder.rb
92
+ - lib/table_structure/schema/keys_builder.rb
93
+ - lib/table_structure/schema/row_context_builder_factory.rb
96
94
  - lib/table_structure/schema/utils.rb
97
95
  - lib/table_structure/table.rb
98
- - lib/table_structure/table/column_converter.rb
99
- - lib/table_structure/table/context_builder.rb
100
- - lib/table_structure/table/row_builder.rb
96
+ - lib/table_structure/utils.rb
101
97
  - lib/table_structure/version.rb
102
98
  - lib/table_structure/writer.rb
103
99
  - table_structure.gemspec
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableStructure
4
- module Schema
5
- module ColumnConverter
6
- class << self
7
- def create_optional_converters(
8
- name_prefix: nil,
9
- name_suffix: nil
10
- )
11
- column_converters = {}
12
-
13
- if name_prefix
14
- column_converters[:_name_prepender_] =
15
- create_prepender(name_prefix, header: true, body: false)
16
- end
17
-
18
- if name_suffix
19
- column_converters[:_name_appender_] =
20
- create_appender(name_suffix, header: true, body: false)
21
- end
22
-
23
- column_converters
24
- end
25
-
26
- private
27
-
28
- def create_prepender(string, **options)
29
- Definition::ColumnConverter.new(
30
- **options
31
- ) do |val, *|
32
- val.nil? ? val : "#{string}#{val}"
33
- end
34
- end
35
-
36
- def create_appender(string, **options)
37
- Definition::ColumnConverter.new(
38
- **options
39
- ) do |val, *|
40
- val.nil? ? val : "#{val}#{string}"
41
- end
42
- end
43
- end
44
- end
45
- end
46
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableStructure
4
- module Schema
5
- module Definition
6
- class ColumnConverter
7
- extend Forwardable
8
-
9
- def_delegator :@callable, :call
10
-
11
- def initialize(
12
- header: true,
13
- body: true,
14
- &block
15
- )
16
- @applicable_to_header = header
17
- @applicable_to_body = body
18
- @callable = block
19
- end
20
-
21
- def applicable_to_header?
22
- !!@applicable_to_header
23
- end
24
-
25
- def applicable_to_body?
26
- !!@applicable_to_body
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableStructure
4
- module Schema
5
- module Definition
6
- class ContextBuilder
7
- extend Forwardable
8
-
9
- def_delegator :@callable, :call
10
-
11
- def initialize(&block)
12
- @callable = block
13
- end
14
- end
15
- end
16
- end
17
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableStructure
4
- module Schema
5
- module Definition
6
- class RowBuilder
7
- extend Forwardable
8
-
9
- def_delegator :@callable, :call
10
-
11
- def initialize(
12
- enabled_row_types: %i[array hash],
13
- &block
14
- )
15
- @enabled_row_types = [enabled_row_types].flatten
16
- @callable = block
17
- end
18
-
19
- def enabled?(row_type)
20
- @enabled_row_types.include?(row_type)
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableStructure
4
- module Schema
5
- module DSL
6
- module ColumnConverter
7
- def column_converter(
8
- name,
9
- header: true,
10
- body: true,
11
- &block
12
- )
13
- column_converters[name] =
14
- ::TableStructure::Schema::Definition::ColumnConverter.new(
15
- header: header,
16
- body: body,
17
- &block
18
- )
19
- nil
20
- end
21
-
22
- def column_converters
23
- @__column_converters__ ||= {}
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableStructure
4
- module Schema
5
- module RowBuilder
6
- DEFAULT_ROW_BUILDERS = {
7
- _to_hash_: Definition::RowBuilder.new(
8
- enabled_row_types: [:hash]
9
- ) do |values, keys, *|
10
- keys.map.with_index { |key, i| [key || i, values[i]] }.to_h
11
- end
12
- }.freeze
13
-
14
- class << self
15
- def prepend_default_builders(builders)
16
- DEFAULT_ROW_BUILDERS.merge(builders)
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableStructure
4
- class Table::ColumnConverter
5
- class ColumnConvertible < Module
6
- def initialize(methods)
7
- methods.each do |name, method|
8
- define_method(name, &method)
9
- end
10
- end
11
- end
12
-
13
- class << self
14
- def create_module(converters, context:)
15
- return if converters.empty?
16
-
17
- header_converters = converters.select { |_k, v| v.applicable_to_header? }
18
- body_converters = converters.select { |_k, v| v.applicable_to_body? }
19
-
20
- methods = {}
21
-
22
- unless header_converters.empty?
23
- methods[:header] = create_method(header_converters, context)
24
- end
25
-
26
- unless body_converters.empty?
27
- methods[:data] = create_method(body_converters, context)
28
- end
29
-
30
- yield ColumnConvertible.new(methods)
31
- end
32
-
33
- private
34
-
35
- def create_method(converters, table_context)
36
- proc do |context: nil|
37
- super(context: context).map do |val|
38
- converters.reduce(val) do |val, (_, converter)|
39
- converter.call(val, context, table_context)
40
- end
41
- end
42
- end
43
- end
44
- end
45
- end
46
- end
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableStructure
4
- class Table::ContextBuilder
5
- class ContextBuildable < Module
6
- def initialize(methods)
7
- methods.each do |name, method|
8
- define_method(name, &method)
9
- end
10
- end
11
- end
12
-
13
- class << self
14
- def create_module(
15
- builders,
16
- apply_to_name: true,
17
- apply_to_value: true,
18
- context:
19
- )
20
- return if builders.empty?
21
-
22
- header_builder = builders[:header] # TODO: Change not to use keyword of `header`
23
- row_builder = builders[:row]
24
-
25
- methods = {}
26
-
27
- if apply_to_name && builders.key?(:header)
28
- methods[:header] = create_method(builders[:header])
29
- end
30
-
31
- if apply_to_value && builders.key?(:row)
32
- methods[:data] = create_method(builders[:row])
33
- end
34
-
35
- yield ContextBuildable.new(methods)
36
- end
37
-
38
- private
39
-
40
- def create_method(builder)
41
- return if builder.nil?
42
-
43
- proc do |context: nil|
44
- super(context: builder.call(context))
45
- end
46
- end
47
- end
48
- end
49
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableStructure
4
- class Table::RowBuilder
5
- class ResultBuildable < Module
6
- def initialize(methods)
7
- methods.each do |name, method|
8
- define_method(name, &method)
9
- end
10
- end
11
- end
12
-
13
- class << self
14
- def create_module(builders, row_type:, keys:, context:)
15
- return if builders.empty?
16
-
17
- builders = builders.select { |_k, v| v.enabled?(row_type) }
18
- return if builders.empty?
19
-
20
- yield ResultBuildable.new(
21
- header: create_method(builders, keys, context),
22
- data: create_method(builders, keys, context)
23
- )
24
- end
25
-
26
- private
27
-
28
- def create_method(builders, table_keys, table_context)
29
- proc do |context: nil|
30
- builders
31
- .reduce(super(context: context)) do |vals, (_, builder)|
32
- builder.call(vals, table_keys, context, table_context)
33
- end
34
- end
35
- end
36
- end
37
- end
38
- end