table_structure 0.4.1 → 0.4.2

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