table_structure 0.3.20 → 0.3.21

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6070401fcd9287249b329ea8f2af80cd6a4f7bd3b758d983fa5aa7582344ece9
4
- data.tar.gz: 717a02a0ed5146dae1054d8b606528c0673e1c5704c7825d0de6bf6385289791
3
+ metadata.gz: c8d52660a94da1c1fbcc8a14a8332214c32ba1627a50239ee72f9916e12c8502
4
+ data.tar.gz: 2156f6ebaa0ee6fd88dfb282a3c339e20101da8bf33cd250355eff7557915921
5
5
  SHA512:
6
- metadata.gz: 3b07692b723c4c70363e93f91a680f8c0e1fd0d0d53e4925ffc758f11afe19a2134fcadae500fab54d6019ef0f92fb35c8a7f1b3c60923037628221c5816a2d2
7
- data.tar.gz: b2b577a2052191f607c9f87c16606650fc565176207a13adf639a6b1b456d8d9acb053ec2788f4fcf523c82e360b28e9d00c2c30e529fa9bfafaefe5e367f5bc
6
+ metadata.gz: 9e5091c8a9c362a09b563e608cf78b74fddb53526ebd53ef30700f4c975429689c5ab8e27f72918d7c4fbdcd798b7911f20dd70593a313fe59bdb88d5deb8e35
7
+ data.tar.gz: 6ae2d481a839d63271009435173250f929009bd2273a59f3aaa974cc14e7612d7cb55258c82c2fe189f304277249d298e0eaaa7d15d1d20b6acc53cee3bb02f9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # 0.3.21
2
+ Changes:
3
+ - Add `TableStructure::Table`
4
+ - This provides methods for converting data with the schema.
5
+ - Use `TableStructure::Table.new` instead of `TableStructure::Schema#create_table`.
6
+ - `TableStructure::Schema`
7
+ - `TableStructure::Schema#create_table` has been deprecated. Use `TableStructure::Table.new` instead.
8
+
1
9
  # 0.3.20
2
10
  Changes:
3
11
  - `TableStructure::Schema`
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- table_structure (0.3.20)
4
+ table_structure (0.3.21)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -8,6 +8,8 @@
8
8
  - Converts data with the schema, and outputs table structured data.
9
9
  - `TableStructure::Iterator`
10
10
  - Converts data with the schema, and enumerates table structured data.
11
+ - `TableStructure::Table`
12
+ - Provides methods for converting data with the schema.
11
13
 
12
14
  ## Installation
13
15
 
@@ -127,7 +129,6 @@ response_body = Enumerator.new do |y|
127
129
  writer.write(items, to: CSV.new(y))
128
130
  end
129
131
  ```
130
- [Sample with docker](https://github.com/jsmmr/ruby_table_structure_sample)
131
132
 
132
133
  You can also convert CSV character code:
133
134
  ```ruby
@@ -147,27 +148,31 @@ end
147
148
  ```
148
149
 
149
150
  #### TableStructure::Iterator
150
- Specifying `row_type: :hash` option works well.
151
- To use this option, define `column(s)` with `:key`.
151
+ If you want to convert the item to row as Hash instead of Array, specify `row_type: :hash`.
152
+ To use this option, define `:key` on `column(s)`.
152
153
 
153
154
  Define a schema:
154
155
  ```ruby
155
156
  class SampleTableSchema
156
157
  include TableStructure::Schema
157
158
 
158
- # If header is required, :name must also be defined.
159
- column key: :id,
159
+ # If `:header` is set to `false`, `:name` is optional.
160
+ column name: 'ID',
161
+ key: :id,
160
162
  value: ->(row, *) { row[:id] }
161
163
 
162
- column key: :name,
164
+ column name: 'Name',
165
+ key: :name,
163
166
  value: ->(row, *) { row[:name] }
164
167
 
165
- columns key: %i[pet1 pet2 pet3],
168
+ columns name: ['Pet 1', 'Pet 2', 'Pet 3'],
169
+ key: %i[pet1 pet2 pet3],
166
170
  value: ->(row, *) { row[:pets] }
167
171
 
168
172
  columns ->(table) {
169
173
  table[:questions].map do |question|
170
174
  {
175
+ name: question[:id],
171
176
  key: question[:id].downcase.to_sym,
172
177
  value: ->(row, *) { row[:answers][question[:id]] }
173
178
  }
@@ -175,7 +180,7 @@ class SampleTableSchema
175
180
  }
176
181
 
177
182
  ## If the schemas are nested, :key must be unique in parent and child schemas.
178
- ## This can also be avoided by specifying :key_prefix or :key_suffix option.
183
+ ## This can also be avoided by using :key_prefix or :key_suffix option.
179
184
  # columns ->(table) { NestedTableSchema.new(context: table, key_prefix: 'foo_', key_suffix: '_bar') }
180
185
  end
181
186
  ```
@@ -191,7 +196,7 @@ context = {
191
196
  }
192
197
 
193
198
  schema = SampleTableSchema.new(context: context)
194
- iterator = TableStructure::Iterator.new(schema, row_type: :hash, header: false)
199
+ iterator = TableStructure::Iterator.new(schema, header: false, row_type: :hash)
195
200
  ```
196
201
 
197
202
  Enumerate the items converted by the schema:
@@ -225,6 +230,33 @@ enum.lazy.select { |item| item[:q1] == '⭕️' }.take(1).force
225
230
  # => [{:id=>1, :name=>"Taro", :pet1=>"🐱", :pet2=>"🐶", :pet3=>nil, :q1=>"⭕️", :q2=>"❌", :q3=>"⭕️"}]
226
231
  ```
227
232
 
233
+ #### TableStructure::Table
234
+
235
+ Initialize a table with the schema and render the table:
236
+ ```erb
237
+ <% TableStructure::Table.new(schema, row_type: :hash) do |table| %>
238
+ <table>
239
+ <thead>
240
+ <tr>
241
+ <% table.header.each do |key, value| %>
242
+ <th class="<%= key %>"><%= value %></th>
243
+ <% end %>
244
+ </tr>
245
+ </thead>
246
+
247
+ <tbody>
248
+ <% table.body(@items).each do |row| %>
249
+ <tr>
250
+ <% row.each do |key, value| %>
251
+ <td class="<%= key %>"><%= value %></td>
252
+ <% end %>
253
+ </tr>
254
+ <% end %>
255
+ </tbody>
256
+ </table>
257
+ <% end %>
258
+ ```
259
+
228
260
  ### Advanced
229
261
 
230
262
  You can add definitions when initializing the schema.
@@ -244,7 +276,7 @@ schema = UserTableSchema.new do
244
276
  end
245
277
  ```
246
278
 
247
- You can also omit columns by defining `:omitted`.
279
+ You can also omit columns by using `:omitted`.
248
280
  ```ruby
249
281
  class UserTableSchema
250
282
  include TableStructure::Schema
@@ -265,7 +297,7 @@ context = { admin: true }
265
297
  schema = UserTableSchema.new(context: context)
266
298
  ```
267
299
 
268
- You can also omit columns by specifying `nil_definitions_ignored: true`.
300
+ You can also omit columns by using `:nil_definitions_ignored` option.
269
301
  If this option is set to `true` and `column(s)` difinition returns `nil`, the difinition is ignored.
270
302
  ```ruby
271
303
  class SampleTableSchema
@@ -327,37 +359,27 @@ end
327
359
  class SampleTableSchema
328
360
  include TableStructure::Schema
329
361
 
330
- columns ->(table) { UserTableSchema.new(context: table) }
362
+ columns UserTableSchema
331
363
  ## or
332
- # columns UserTableSchema
364
+ # columns ->(table) { UserTableSchema.new(context: table) }
333
365
 
334
- columns ->(table) { PetTableSchema.new(context: table) }
366
+ columns PetTableSchema
335
367
  ## or
336
- # columns PetTableSchema
368
+ # columns ->(table) { PetTableSchema.new(context: table) }
337
369
 
338
- columns ->(table) { QuestionTableSchema.new(context: table) }
370
+ columns QuestionTableSchema
339
371
  ## or
340
- # columns QuestionTableSchema
372
+ # columns ->(table) { QuestionTableSchema.new(context: table) }
341
373
  end
342
-
343
- context = {
344
- questions: [
345
- { id: 'Q1', text: 'Do you like sushi?' },
346
- { id: 'Q2', text: 'Do you like yakiniku?' },
347
- { id: 'Q3', text: 'Do you like ramen?' }
348
- ]
349
- }
350
-
351
- schema = SampleTableSchema.new(context: context)
352
374
  ```
353
375
 
354
376
  You can also concatenate or merge the schema classes.
355
377
  Both create a schema class, with a few differences.
356
378
  - `+`
357
379
  - Similar to nesting the schemas.
358
- `column_converter` or `context_builder` works only to columns in the schema that they was defined.
380
+ `column_converter` works only to columns in the schema that they was defined.
359
381
  - `merge`
360
- - If there are some definitions of `column_converter` or `context_builder` with the same name in the schemas to be merged, the one in the schema that is merged last will work to all columns.
382
+ - If there are some definitions of `column_converter` with the same name in the schemas to be merged, the one in the schema that is merged last will work to all columns.
361
383
 
362
384
  ```ruby
363
385
  class UserTableSchema
@@ -444,30 +466,9 @@ class SampleTableSchema
444
466
  end
445
467
  ```
446
468
 
447
- You can also use only `TableStructure::Schema` instance.
448
- ```erb
449
- <% @schema.create_table(row_type: :hash) do |table| %>
450
- <table>
451
- <thead>
452
- <tr>
453
- <% table.header.each do |key, value| %>
454
- <th class="<%= key %>"><%= value %></th>
455
- <% end %>
456
- </tr>
457
- </thead>
469
+ ## Sample with docker
458
470
 
459
- <tbody>
460
- <% table.body(@items).each do |row| %>
461
- <tr>
462
- <% row.each do |key, value| %>
463
- <td class="<%= key %>"><%= value %></td>
464
- <% end %>
465
- </tr>
466
- <% end %>
467
- </tbody>
468
- </table>
469
- <% end %>
470
- ```
471
+ https://github.com/jsmmr/ruby_table_structure_sample
471
472
 
472
473
  ## Contributing
473
474
 
@@ -21,14 +21,16 @@ module TableStructure
21
21
  require 'table_structure/schema/definition/columns/attributes'
22
22
  require 'table_structure/schema/definition/columns/schema_class'
23
23
  require 'table_structure/schema/definition/columns/schema_instance'
24
- require 'table_structure/schema/column_converters'
25
- require 'table_structure/schema/context_builders'
26
- require 'table_structure/schema/row_builders'
27
- require 'table_structure/schema/keys_generator'
28
- require 'table_structure/schema/table'
24
+ require 'table_structure/schema/column_converter'
25
+ require 'table_structure/schema/row_builder'
26
+ require 'table_structure/schema/key_converter'
29
27
  require 'table_structure/schema/columns/attributes'
30
28
  require 'table_structure/schema/columns/schema'
31
29
  require 'table_structure/schema/utils'
30
+ require 'table_structure/table'
31
+ require 'table_structure/table/column_converter'
32
+ require 'table_structure/table/context_builder'
33
+ require 'table_structure/table/row_builder'
32
34
  require 'table_structure/table/iterator'
33
35
  require 'table_structure/writer'
34
36
  require 'table_structure/csv/writer'
@@ -2,21 +2,106 @@
2
2
 
3
3
  module TableStructure
4
4
  class Iterator
5
- def initialize(schema_or_writer, **options)
6
- if schema_or_writer.is_a?(Schema)
7
- schema = schema_or_writer
8
- @writer = Writer.new(schema, **options)
9
- elsif schema_or_writer.is_a?(Writer)
10
- warn "[TableStructure] Pass Writer as an argument has been deprecated. Pass Schema instead. #{caller_locations(1, 1)}"
11
- @writer = schema_or_writer
12
- else
13
- raise ::TableStructure::Error, "Must be either Schema or Writer. #{schema_or_writer}"
5
+ def initialize(
6
+ schema,
7
+ header: { context: nil },
8
+ row_type: :array,
9
+ **deprecated_options
10
+ )
11
+ if deprecated_options.key?(:header_omitted)
12
+ header_omitted = deprecated_options[:header_omitted]
13
+ warn "[TableStructure] `header_omitted: #{!!header_omitted}` option has been deprecated. Use `header: #{!header_omitted}` option instead."
14
+ header = !header_omitted
15
+ end
16
+
17
+ if deprecated_options.key?(:header_context)
18
+ header_context = deprecated_options[:header_context]
19
+ warn '[TableStructure] `:header_context` option has been deprecated. Use `header: { context: ... }` option instead.'
20
+ header = { context: header_context }
21
+ end
22
+
23
+ if deprecated_options.key?(:result_type)
24
+ warn '[TableStructure] `:result_type` option has been deprecated. Use `:row_type` option instead.'
25
+ row_type = deprecated_options[:result_type]
26
+ end
27
+
28
+ unless schema.is_a?(Schema)
29
+ raise ::TableStructure::Error, "Must be use Schema. #{schema}"
30
+ end
31
+
32
+ @schema = schema
33
+ @options = {
34
+ header: header,
35
+ row_type: row_type
36
+ }
37
+ end
38
+
39
+ def iterate(
40
+ items,
41
+ **deprecated_options,
42
+ &block
43
+ )
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
14
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
+ items = enumerize(items)
75
+
76
+ enum =
77
+ Table::Iterator
78
+ .new(
79
+ Table.new(@schema, row_type: row_type),
80
+ header: header
81
+ )
82
+ .iterate(items)
83
+
84
+ if block_given?
85
+ enum =
86
+ enum
87
+ .lazy
88
+ .map { |row| block.call(row) }
89
+ end
90
+
91
+ enum
15
92
  end
16
93
 
17
- def iterate(items, **options, &block)
18
- # TODO: Change not to use Writer.
19
- Enumerator.new { |y| @writer.write(items, to: y, **options, &block) }
94
+ private
95
+
96
+ def enumerize(items)
97
+ if items.respond_to?(:each)
98
+ items
99
+ elsif items.respond_to?(:call)
100
+ warn "[TableStructure] Use `Enumerator` to wrap items instead of `lambda`. The use of `lambda` has been deprecated. #{items}"
101
+ Enumerator.new { |y| items.call(y) }
102
+ else
103
+ raise ::TableStructure::Error, "Must be enumerable. #{items}"
104
+ end
20
105
  end
21
106
  end
22
107
  end
@@ -21,15 +21,12 @@ module TableStructure
21
21
  end
22
22
  end
23
23
 
24
- MyDefinition = Struct.new(
25
- :name,
26
- :columns,
27
- :context_builders,
28
- :column_converters,
29
- :row_builders,
30
- :context,
31
- :options
32
- )
24
+ attr_reader :columns,
25
+ :context_builders,
26
+ :column_converters,
27
+ :key_converter,
28
+ :row_builders,
29
+ :context
33
30
 
34
31
  def initialize(
35
32
  name: self.class.name,
@@ -42,20 +39,27 @@ module TableStructure
42
39
  **deprecated_options,
43
40
  &block
44
41
  )
45
- unless deprecated_options.empty?
46
- caller_location = caller_locations(1, 1)
47
- deprecated_options.keys.each do |k|
48
- warn "[TableStructure] Specify :#{k} option on Writer or Iterator. #{caller_location}"
49
- end
42
+ if deprecated_options.key?(:row_type)
43
+ raise ::TableStructure::Error, 'Use :row_type option with Table, Writer or Iterator.'
44
+ end
45
+
46
+ if deprecated_options.key?(:result_type)
47
+ raise ::TableStructure::Error, ':result_type option has been deprecated. Use :row_type option instead.'
50
48
  end
51
49
 
52
- options = {
53
- name_prefix: name_prefix,
54
- name_suffix: name_suffix,
55
- key_prefix: key_prefix,
56
- key_suffix: key_suffix,
57
- nil_definitions_ignored: nil_definitions_ignored
58
- }.merge!(self.class.options).merge!(deprecated_options)
50
+ options =
51
+ [
52
+ self.class.options,
53
+ {
54
+ name_prefix: name_prefix,
55
+ name_suffix: name_suffix,
56
+ key_prefix: key_prefix,
57
+ key_suffix: key_suffix,
58
+ nil_definitions_ignored: nil_definitions_ignored
59
+ },
60
+ deprecated_options
61
+ ]
62
+ .reduce({}, &:merge!)
59
63
 
60
64
  schema_classes = [self.class]
61
65
 
@@ -63,90 +67,65 @@ module TableStructure
63
67
  schema_classes << ::TableStructure::Schema.create_class(&block)
64
68
  end
65
69
 
66
- context_builders = ContextBuilders.new(
67
- schema_classes.map(&:context_builders).reduce({}, &:merge!)
68
- )
70
+ @context_builders =
71
+ schema_classes
72
+ .map(&:context_builders)
73
+ .reduce({}, &:merge!)
69
74
 
70
- column_converters = ColumnConverters.new(
71
- schema_classes.map(&:column_converters).reduce({}, &:merge!)
72
- )
75
+ table_context_builder = @context_builders.delete(:table)
76
+
77
+ @context = table_context_builder ? table_context_builder.call(context) : context
73
78
 
74
- row_builders = RowBuilders.new(
75
- schema_classes.map(&:row_builders).reduce({}, &:merge!)
79
+ @column_converters =
80
+ schema_classes
81
+ .map(&:column_converters)
82
+ .reduce({}, &:merge!)
83
+ .merge(
84
+ ColumnConverter.create_optional_converters(
85
+ name_prefix: options.delete(:name_prefix),
86
+ name_suffix: options.delete(:name_suffix)
87
+ )
88
+ )
89
+
90
+ @key_converter = KeyConverter.new(
91
+ prefix: options.delete(:key_prefix),
92
+ suffix: options.delete(:key_suffix)
76
93
  )
77
94
 
78
- table_context = context_builders.build_for_table(context)
95
+ @row_builders =
96
+ RowBuilder.prepend_default_builders(
97
+ schema_classes
98
+ .map(&:row_builders)
99
+ .reduce({}, &:merge!)
100
+ )
79
101
 
80
- columns =
102
+ @columns =
81
103
  Definition::Columns::Compiler
82
104
  .new(
83
105
  name,
84
106
  schema_classes.map(&:column_definitions).reduce([], &:concat),
85
- options
107
+ { nil_definitions_ignored: options.delete(:nil_definitions_ignored) }
86
108
  )
87
- .compile(table_context)
109
+ .compile(@context)
88
110
 
89
- @_definition_ =
90
- MyDefinition.new(
91
- name,
92
- columns,
93
- context_builders,
94
- column_converters,
95
- row_builders,
96
- table_context,
97
- options
98
- )
111
+ @options = options
99
112
  end
100
113
 
101
- def create_table(row_type: :array, **deprecated_options)
102
- options = @_definition_.options.merge(deprecated_options)
114
+ def create_table(row_type: :array, **deprecated_options, &block)
115
+ warn '[TableStructure] `TableStructure::Schema#create_table` has been deprecated. Use `TableStructure::Table.new` instead.'
116
+
117
+ options = @options.merge(deprecated_options)
103
118
 
104
119
  if options.key?(:result_type)
105
120
  warn '[TableStructure] `:result_type` option has been deprecated. Use `:row_type` option instead.'
106
121
  options[:row_type] = options[:result_type]
107
122
  end
108
123
 
109
- keys_generator_options = {
110
- prefix: options[:key_prefix],
111
- suffix: options[:key_suffix]
112
- }
113
-
114
- keys_generator = KeysGenerator.new(
115
- **keys_generator_options
116
- )
117
-
118
- table = Table.new(
119
- columns: @_definition_.columns,
120
- context: @_definition_.context,
121
- keys_generator: keys_generator
122
- )
123
-
124
- @_definition_
125
- .context_builders
126
- .extend_methods_for(table)
127
-
128
- column_converters_options = {
129
- name_prefix: options[:name_prefix],
130
- name_suffix: options[:name_suffix]
131
- }
132
-
133
- @_definition_
134
- .column_converters
135
- .extend_methods_for(table, **column_converters_options)
136
-
137
- row_builders_options = {
138
- row_type: options[:row_type] || row_type
139
- }
140
-
141
- @_definition_
142
- .row_builders
143
- .extend_methods_for(table, **row_builders_options)
124
+ ::TableStructure::Table.new(self, row_type: options[:row_type] || row_type, &block)
125
+ end
144
126
 
145
- if block_given?
146
- yield table
147
- else
148
- table
149
- end
127
+ def contain_callable?(attribute)
128
+ @columns.any? { |column| column.contain_callable?(attribute) }
150
129
  end
151
130
  end
152
131
  end
@@ -0,0 +1,48 @@
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
+ lambda { |val, *|
31
+ val.nil? ? val : "#{string}#{val}"
32
+ },
33
+ **options
34
+ )
35
+ end
36
+
37
+ def create_appender(string, **options)
38
+ Definition::ColumnConverter.new(
39
+ lambda { |val, *|
40
+ val.nil? ? val : "#{val}#{string}"
41
+ },
42
+ **options
43
+ )
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -5,7 +5,8 @@ module TableStructure
5
5
  module Columns
6
6
  class Schema
7
7
  def initialize(schema)
8
- @table = schema.create_table
8
+ @schema = schema
9
+ @table = ::TableStructure::Table.new(schema)
9
10
  end
10
11
 
11
12
  def names(header_context, _table_context)
@@ -25,7 +26,7 @@ module TableStructure
25
26
  end
26
27
 
27
28
  def contain_callable?(attribute)
28
- @table.send(:contain_callable?, attribute)
29
+ @schema.contain_callable?(attribute)
29
30
  end
30
31
  end
31
32
  end
@@ -2,13 +2,13 @@
2
2
 
3
3
  module TableStructure
4
4
  module Schema
5
- class KeysGenerator
5
+ class KeyConverter
6
6
  def initialize(prefix: nil, suffix: nil)
7
7
  @prefix = prefix
8
8
  @suffix = suffix
9
9
  end
10
10
 
11
- def generate(keys)
11
+ def convert(keys)
12
12
  return keys unless has_any_options?
13
13
 
14
14
  keys.map do |key|
@@ -0,0 +1,22 @@
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
+ lambda { |values, keys, *|
9
+ keys.map.with_index { |key, i| [key || i, values[i]] }.to_h
10
+ },
11
+ enabled_row_types: [:hash]
12
+ )
13
+ }.freeze
14
+
15
+ class << self
16
+ def prepend_default_builders(builders)
17
+ DEFAULT_ROW_BUILDERS.merge(builders)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TableStructure
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 }
21
+
22
+ RowBuilder.create_module(
23
+ schema.row_builders,
24
+ row_type: row_type,
25
+ keys: keys,
26
+ context: schema.context
27
+ ) { |mod| extend mod }
28
+
29
+ yield self if block_given?
30
+ end
31
+
32
+ def header(context: nil)
33
+ row_values(:names, context)
34
+ end
35
+
36
+ def body(items)
37
+ Enumerator.new do |y|
38
+ items.each { |item| y << data(context: item) }
39
+ end
40
+ 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
+ end
72
+ end
@@ -0,0 +1,46 @@
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
@@ -0,0 +1,49 @@
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] # will remove
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,23 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TableStructure
4
- module Table
5
- class Iterator
6
- def initialize(table, header: { context: nil })
7
- @table = table
8
- @header_options = header
9
- end
4
+ class Table::Iterator
5
+ def initialize(table, header: { context: nil })
6
+ @table = table
7
+ @header_options = header
8
+ end
10
9
 
11
- def iterate(items)
12
- ::Enumerator.new do |y|
13
- if @header_options
14
- header_context = @header_options.is_a?(Hash) ? @header_options[:context] : nil
15
- y << @table.header(context: header_context)
16
- end
17
- @table
18
- .body(items)
19
- .each { |row| y << row }
10
+ def iterate(items)
11
+ ::Enumerator.new do |y|
12
+ if @header_options
13
+ header_context = @header_options.is_a?(Hash) ? @header_options[:context] : nil
14
+ y << @table.header(context: header_context)
20
15
  end
16
+ @table
17
+ .body(items)
18
+ .each { |row| y << row }
21
19
  end
22
20
  end
23
21
  end
@@ -0,0 +1,38 @@
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TableStructure
4
- VERSION = '0.3.20'
4
+ VERSION = '0.3.21'
5
5
  end
@@ -46,7 +46,7 @@ module TableStructure
46
46
 
47
47
  if deprecated_options.key?(:header)
48
48
  header = deprecated_options[:header]
49
- warn '[TableStructure] Specify :header option as an argument for initialize method.'
49
+ warn '[TableStructure] Use :header option on initialize method.'
50
50
  end
51
51
 
52
52
  if deprecated_options.key?(:header_omitted)
@@ -63,7 +63,7 @@ module TableStructure
63
63
 
64
64
  if deprecated_options.key?(:row_type)
65
65
  row_type = deprecated_options[:row_type]
66
- warn '[TableStructure] Specify :row_type option as an argument for initialize method.'
66
+ warn '[TableStructure] Use :row_type option on initialize method.'
67
67
  end
68
68
 
69
69
  if deprecated_options.key?(:result_type)
@@ -71,28 +71,13 @@ module TableStructure
71
71
  row_type = deprecated_options[:result_type]
72
72
  end
73
73
 
74
- items = enumerize(items)
74
+ output = Output.new(to, method: method)
75
75
 
76
- @schema.create_table(row_type: row_type) do |table|
77
- output = Output.new(to, method: method)
76
+ Iterator
77
+ .new(@schema, header: header, row_type: row_type)
78
+ .iterate(items, &block)
79
+ .each { |row| output.write(row) }
78
80
 
79
- enum =
80
- Table::Iterator
81
- .new(
82
- table,
83
- header: header
84
- )
85
- .iterate(items)
86
-
87
- if block_given?
88
- enum =
89
- enum
90
- .lazy
91
- .map { |row| block.call(row) }
92
- end
93
-
94
- enum.each { |row| output.write(row) }
95
- end
96
81
  nil
97
82
  end
98
83
 
@@ -108,16 +93,5 @@ module TableStructure
108
93
  @output.send(@method, values)
109
94
  end
110
95
  end
111
-
112
- def enumerize(items)
113
- if items.respond_to?(:each)
114
- items
115
- elsif items.respond_to?(:call)
116
- warn "[TableStructure] Use `Enumerator` to wrap items instead of `lambda`. The use of `lambda` has been deprecated. #{items}"
117
- Enumerator.new { |y| items.call(y) }
118
- else
119
- raise ::TableStructure::Error, "Must be enumerable. #{items}"
120
- end
121
- end
122
96
  end
123
97
  end
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.3.20
4
+ version: 0.3.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - jsmmr
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-02-29 00:00:00.000000000 Z
11
+ date: 2020-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,10 +75,9 @@ 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_converters.rb
78
+ - lib/table_structure/schema/column_converter.rb
79
79
  - lib/table_structure/schema/columns/attributes.rb
80
80
  - lib/table_structure/schema/columns/schema.rb
81
- - lib/table_structure/schema/context_builders.rb
82
81
  - lib/table_structure/schema/definition/column_converter.rb
83
82
  - lib/table_structure/schema/definition/columns/attributes.rb
84
83
  - lib/table_structure/schema/definition/columns/compiler.rb
@@ -93,11 +92,14 @@ files:
93
92
  - lib/table_structure/schema/dsl/context_builder.rb
94
93
  - lib/table_structure/schema/dsl/option.rb
95
94
  - lib/table_structure/schema/dsl/row_builder.rb
96
- - lib/table_structure/schema/keys_generator.rb
97
- - lib/table_structure/schema/row_builders.rb
98
- - lib/table_structure/schema/table.rb
95
+ - lib/table_structure/schema/key_converter.rb
96
+ - lib/table_structure/schema/row_builder.rb
99
97
  - lib/table_structure/schema/utils.rb
98
+ - lib/table_structure/table.rb
99
+ - lib/table_structure/table/column_converter.rb
100
+ - lib/table_structure/table/context_builder.rb
100
101
  - lib/table_structure/table/iterator.rb
102
+ - lib/table_structure/table/row_builder.rb
101
103
  - lib/table_structure/version.rb
102
104
  - lib/table_structure/writer.rb
103
105
  - table_structure.gemspec
@@ -1,83 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableStructure
4
- module Schema
5
- class ColumnConverters
6
- def initialize(converters)
7
- @header_converters = converters.select { |_k, v| v.applicable_to_header? }
8
- @body_converterss = converters.select { |_k, v| v.applicable_to_body? }
9
- end
10
-
11
- def extend_methods_for(table, name_prefix:, name_suffix:)
12
- table_context = table.instance_variable_get(:@context)
13
-
14
- header_converters =
15
- @header_converters
16
- .merge(
17
- _prepend_prefix_: create_prepender(name_prefix),
18
- _append_suffix_: create_appender(name_suffix)
19
- )
20
- .reject { |_k, v| v.nil? }
21
-
22
- body_converterss = @body_converterss
23
-
24
- methods =
25
- {
26
- header: create_method(header_converters, table_context),
27
- data: create_method(body_converterss, table_context)
28
- }
29
- .reject { |_k, v| v.nil? }
30
-
31
- return if methods.empty?
32
-
33
- table.extend ColumnConvertible.new(methods)
34
- end
35
-
36
- private
37
-
38
- def create_prepender(prefix)
39
- return unless prefix
40
-
41
- Definition::ColumnConverter.new(
42
- lambda { |val, *|
43
- val.nil? ? val : "#{prefix}#{val}"
44
- },
45
- header: true,
46
- body: false
47
- )
48
- end
49
-
50
- def create_appender(suffix)
51
- return unless suffix
52
-
53
- Definition::ColumnConverter.new(
54
- lambda { |val, *|
55
- val.nil? ? val : "#{val}#{suffix}"
56
- },
57
- header: true,
58
- body: false
59
- )
60
- end
61
-
62
- def create_method(converters, table_context)
63
- return if converters.empty?
64
-
65
- proc do |context: nil|
66
- super(context: context).map do |val|
67
- converters.reduce(val) do |val, (_, converter)|
68
- converter.call(val, context, table_context)
69
- end
70
- end
71
- end
72
- end
73
- end
74
-
75
- class ColumnConvertible < Module
76
- def initialize(methods)
77
- methods.each do |name, method|
78
- define_method(name, &method)
79
- end
80
- end
81
- end
82
- end
83
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableStructure
4
- module Schema
5
- class ContextBuilders
6
- def initialize(builders)
7
- @table_builder = builders[:table]
8
- @header_builder = builders[:header]
9
- @row_builder = builders[:row]
10
- end
11
-
12
- def build_for_table(context)
13
- if @table_builder
14
- @table_builder.call(context)
15
- else
16
- context
17
- end
18
- end
19
-
20
- def extend_methods_for(table)
21
- methods = {}
22
-
23
- if table.send(:contain_callable?, :name)
24
- methods[:header] = create_method(@header_builder)
25
- end
26
- if table.send(:contain_callable?, :value)
27
- methods[:data] = create_method(@row_builder)
28
- end
29
-
30
- methods.reject! { |_k, v| v.nil? }
31
- return if methods.empty?
32
-
33
- table.extend ContextBuildable.new(methods)
34
- end
35
-
36
- private
37
-
38
- def create_method(builder)
39
- return if builder.nil?
40
-
41
- proc do |context: nil|
42
- super(context: builder.call(context))
43
- end
44
- end
45
- end
46
-
47
- class ContextBuildable < Module
48
- def initialize(methods)
49
- methods.each do |name, method|
50
- define_method(name, &method)
51
- end
52
- end
53
- end
54
- end
55
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableStructure
4
- module Schema
5
- class RowBuilders
6
- DEFAULT_BUILDERS = {
7
- _to_hash_: ::TableStructure::Schema::Definition::RowBuilder.new(
8
- lambda { |values, keys, *|
9
- keys.map.with_index { |key, i| [key || i, values[i]] }.to_h
10
- },
11
- enabled_row_types: [:hash]
12
- )
13
- }.freeze
14
-
15
- def initialize(builders)
16
- @builders = builders
17
- end
18
-
19
- def extend_methods_for(table, row_type: :array)
20
- builders =
21
- DEFAULT_BUILDERS
22
- .merge(@builders)
23
- .select { |_k, v| v.enabled?(row_type) }
24
-
25
- return if builders.empty?
26
-
27
- table_context = table.instance_variable_get(:@context)
28
- table_keys = table.send(:keys)
29
-
30
- table.extend ResultBuildable.new(
31
- header: create_method(builders, table_keys, table_context),
32
- data: create_method(builders, table_keys, table_context)
33
- )
34
- end
35
-
36
- private
37
-
38
- def create_method(builders, table_keys, table_context)
39
- return if builders.empty?
40
-
41
- proc do |context: nil|
42
- builders
43
- .reduce(super(context: context)) do |vals, (_, builder)|
44
- builder.call(vals, table_keys, context, table_context)
45
- end
46
- end
47
- end
48
- end
49
-
50
- class ResultBuildable < Module
51
- def initialize(methods)
52
- methods.each do |name, method|
53
- define_method(name, &method)
54
- end
55
- end
56
- end
57
- end
58
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TableStructure
4
- module Schema
5
- class Table
6
- def initialize(
7
- columns:,
8
- context:,
9
- keys_generator:
10
- )
11
- @columns = columns
12
- @context = context
13
- @keys_generator = keys_generator
14
- end
15
-
16
- def header(context: nil)
17
- row_values(:names, context)
18
- end
19
-
20
- def row(context: nil)
21
- warn '[TableStructure] `TableStructure::Schema::Table#row(context:)` has been deprecated. Use `TableStructure::Schema::Table#body(items)` instead.'
22
- data(context: context)
23
- end
24
-
25
- def body(items)
26
- Enumerator.new do |y|
27
- items.each { |item| y << data(context: item) }
28
- end
29
- end
30
-
31
- def rows(items)
32
- warn '[TableStructure] `TableStructure::Schema::Table#rows(items)` has been deprecated. Use `TableStructure::Schema::Table#body(items)` instead.'
33
- body(items)
34
- end
35
-
36
- private
37
-
38
- def data(context: nil)
39
- row_values(:values, context)
40
- end
41
-
42
- def keys
43
- @keys ||= @keys_generator.generate(@columns.map(&:keys).flatten)
44
- end
45
-
46
- def size
47
- @size ||= @columns.map(&:size).reduce(0, &:+)
48
- end
49
-
50
- def row_values(method, context)
51
- @columns
52
- .map { |column| column.send(method, context, @context) }
53
- .flatten
54
- end
55
-
56
- def contain_callable?(attribute)
57
- @columns.any? { |column| column.contain_callable?(attribute) }
58
- end
59
- end
60
- end
61
- end