table_structure 0.3.14 → 0.3.15
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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +2 -2
- data/README.md +55 -6
- data/lib/table_structure/csv/writer.rb +34 -40
- data/lib/table_structure/schema/class_methods.rb +34 -0
- data/lib/table_structure/schema/column/attrs.rb +7 -7
- data/lib/table_structure/schema/column/definition/compiler.rb +81 -0
- data/lib/table_structure/schema/column/definition/error.rb +19 -0
- data/lib/table_structure/schema/column/definition/validator.rb +35 -0
- data/lib/table_structure/schema/column/factory.rb +22 -0
- data/lib/table_structure/schema/column/schema.rb +2 -2
- data/lib/table_structure/schema/column_converters.rb +9 -8
- data/lib/table_structure/schema/dsl/column_converter.rb +1 -1
- data/lib/table_structure/schema/dsl/column_definition.rb +1 -1
- data/lib/table_structure/schema/dsl/context_builder.rb +1 -1
- data/lib/table_structure/schema/dsl/option.rb +1 -1
- data/lib/table_structure/schema/dsl/result_builder.rb +1 -1
- data/lib/table_structure/schema/result_builders.rb +5 -6
- data/lib/table_structure/schema/table.rb +3 -7
- data/lib/table_structure/schema.rb +52 -9
- data/lib/table_structure/version.rb +1 -1
- data/lib/table_structure.rb +5 -4
- metadata +8 -7
- data/lib/table_structure/schema/definition/compiler.rb +0 -79
- data/lib/table_structure/schema/definition/error.rb +0 -17
- data/lib/table_structure/schema/definition/validator.rb +0 -33
- data/lib/table_structure/schema/definition.rb +0 -63
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77197fdbfa164e9f532b190b4e94750354595f9e51d155b91ed36ea2f43aeef2
|
4
|
+
data.tar.gz: 52ab0ab962584d0d7e8e10788a6c949369926ec8869577e797a462e90eb76487
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99774b00f0260b64ed8a3ea670cf24d79e222c8b75fab7545932fbb248516561078f2908a081bae925e7baf44502d115711974d5b7458937e6db1e0961be0152
|
7
|
+
data.tar.gz: ca70303e5f5ec9ae437e9dccd9554b027c48aa68b7cb9c0f0239f6fa1ca0259321c9ea942310ac7a320e8d80ef6b996ef02a1cfb5ac0afa3a317534b351ecd85
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -76,7 +76,7 @@ schema = SampleTableSchema.new(context: context)
|
|
76
76
|
Initialize a writer with the schema:
|
77
77
|
```ruby
|
78
78
|
writer = TableStructure::Writer.new(schema)
|
79
|
-
##
|
79
|
+
## To omit header, write:
|
80
80
|
# writer = TableStructure::Writer.new(schema, header_omitted: true)
|
81
81
|
```
|
82
82
|
|
@@ -97,8 +97,9 @@ items = [
|
|
97
97
|
}
|
98
98
|
]
|
99
99
|
|
100
|
-
##
|
100
|
+
## To use Rails `find_each` method, write:
|
101
101
|
# items = Item.enum_for(:find_each)
|
102
|
+
## or
|
102
103
|
# items = Enumerator.new { |y| Item.find_each { |item| y << item } }
|
103
104
|
|
104
105
|
table = []
|
@@ -117,7 +118,7 @@ end
|
|
117
118
|
|
118
119
|
Writes the items converted by the schema to stream as CSV with Rails:
|
119
120
|
```ruby
|
120
|
-
# response.headers['X-Accel-Buffering'] = 'no' #
|
121
|
+
# response.headers['X-Accel-Buffering'] = 'no' # Required if Nginx is used for reverse proxy
|
121
122
|
response.headers['Cache-Control'] = 'no-cache'
|
122
123
|
response.headers['Content-Type'] = 'text/csv'
|
123
124
|
response.headers['Content-Disposition'] = 'attachment; filename="sample.csv"'
|
@@ -164,7 +165,7 @@ class SampleTableSchema
|
|
164
165
|
end
|
165
166
|
}
|
166
167
|
|
167
|
-
##
|
168
|
+
## If the schemas are nested, :key must be unique in parent and child schemas.
|
168
169
|
## This can also be avoided by specifying :key_prefix or :key_suffix option.
|
169
170
|
# columns ->(table) { NestedSchema.new(context: table, key_prefix: 'foo_', key_suffix: '_bar') }
|
170
171
|
|
@@ -318,8 +319,56 @@ context = {
|
|
318
319
|
schema = SampleTableSchema.new(context: context)
|
319
320
|
```
|
320
321
|
|
322
|
+
You can also concatenate schemas.
|
323
|
+
If there are some definitions of `column_converter` with the same name in the schemas to be concatenated, the one in the schema that is concatenated last will be used.
|
324
|
+
```ruby
|
325
|
+
class UserTableSchema
|
326
|
+
include TableStructure::Schema
|
327
|
+
|
328
|
+
column name: 'ID',
|
329
|
+
value: ->(row, _table) { row[:id] }
|
330
|
+
|
331
|
+
column name: 'Name',
|
332
|
+
value: ->(row, *) { row[:name] }
|
333
|
+
end
|
334
|
+
|
335
|
+
class PetTableSchema
|
336
|
+
include TableStructure::Schema
|
337
|
+
|
338
|
+
columns name: ['Pet 1', 'Pet 2', 'Pet 3'],
|
339
|
+
value: ->(row, *) { row[:pets] }
|
340
|
+
|
341
|
+
column_converter :same_name, ->(val, *) { 'This definition will not be used.' }
|
342
|
+
end
|
343
|
+
|
344
|
+
class QuestionTableSchema
|
345
|
+
include TableStructure::Schema
|
346
|
+
|
347
|
+
columns ->(table) {
|
348
|
+
table[:questions].map do |question|
|
349
|
+
{
|
350
|
+
name: question[:id],
|
351
|
+
value: ->(row, *) { row[:answers][question[:id]] }
|
352
|
+
}
|
353
|
+
end
|
354
|
+
}
|
355
|
+
|
356
|
+
column_converter :same_name, ->(val, *) { 'This definition will be used.' }
|
357
|
+
end
|
358
|
+
|
359
|
+
context = {
|
360
|
+
questions: [
|
361
|
+
{ id: 'Q1', text: 'Do you like sushi?' },
|
362
|
+
{ id: 'Q2', text: 'Do you like yakiniku?' },
|
363
|
+
{ id: 'Q3', text: 'Do you like ramen?' }
|
364
|
+
]
|
365
|
+
}
|
366
|
+
|
367
|
+
schema = (UserTableSchema + PetTableSchema + QuestionTableSchema).new(context: context)
|
368
|
+
```
|
369
|
+
|
321
370
|
You can also use `context_builder`.
|
322
|
-
This may be useful
|
371
|
+
This may be useful if `column(s)` lambda is complicated.
|
323
372
|
```ruby
|
324
373
|
class SampleTableSchema
|
325
374
|
include TableStructure::Schema
|
@@ -359,7 +408,7 @@ If you want to convert CSV character code, you can do so within block of `write`
|
|
359
408
|
```ruby
|
360
409
|
File.open('sample.csv', 'w') do |f|
|
361
410
|
writer.write(items, to: CSV.new(f)) do |row_values|
|
362
|
-
row_values.map { |val| val
|
411
|
+
row_values.map { |val| val.to_s.encode('Shift_JIS', invalid: :replace, undef: :replace) }
|
363
412
|
end
|
364
413
|
end
|
365
414
|
```
|
@@ -3,53 +3,47 @@
|
|
3
3
|
module TableStructure
|
4
4
|
module CSV
|
5
5
|
class Writer
|
6
|
-
DEFAULT_OPTIONS = {
|
7
|
-
bom: false,
|
8
|
-
csv_options: {}
|
9
|
-
}.freeze
|
10
|
-
|
11
|
-
INNER_WRITER_DEFAULT_OPTIONS = ::TableStructure::Writer::DEFAULT_OPTIONS
|
12
|
-
INNER_WRITER_IGNORED_OPTION_KEYS = %i[
|
13
|
-
result_type
|
14
|
-
method
|
15
|
-
].freeze
|
16
|
-
|
17
6
|
BOM = "\uFEFF"
|
18
7
|
|
19
|
-
def initialize(
|
8
|
+
def initialize(
|
9
|
+
schema,
|
10
|
+
bom: false,
|
11
|
+
csv_options: {},
|
12
|
+
header_omitted: false,
|
13
|
+
header_context: nil
|
14
|
+
)
|
20
15
|
require 'csv'
|
21
|
-
@options = DEFAULT_OPTIONS.merge(select_csv_writer_options(options))
|
22
|
-
inner_writer_options = select_inner_writer_options(options)
|
23
|
-
@writer = ::TableStructure::Writer.new(schema, **inner_writer_options)
|
24
|
-
end
|
25
|
-
|
26
|
-
def write(items, to:, **options, &block)
|
27
|
-
csv_writer_options = @options.merge(select_csv_writer_options(options))
|
28
|
-
inner_writer_options = select_inner_writer_options(options)
|
29
|
-
if csv_writer_options[:bom]
|
30
|
-
to.send(INNER_WRITER_DEFAULT_OPTIONS[:method], BOM)
|
31
|
-
end
|
32
|
-
csv = ::CSV.new(to, **csv_writer_options[:csv_options])
|
33
|
-
@writer.write(items, to: csv, **inner_writer_options, &block)
|
34
|
-
end
|
35
16
|
|
36
|
-
|
17
|
+
@options = {
|
18
|
+
bom: bom,
|
19
|
+
csv_options: csv_options
|
20
|
+
}
|
21
|
+
@inner_options = {
|
22
|
+
header_omitted: header_omitted,
|
23
|
+
header_context: header_context
|
24
|
+
}
|
37
25
|
|
38
|
-
|
39
|
-
options
|
40
|
-
.select { |k, _v| DEFAULT_OPTIONS.include?(k) }
|
26
|
+
@writer = ::TableStructure::Writer.new(schema, **@inner_options)
|
41
27
|
end
|
42
28
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
29
|
+
def write(
|
30
|
+
items,
|
31
|
+
to:,
|
32
|
+
bom: @options[:bom],
|
33
|
+
csv_options: @options[:csv_options],
|
34
|
+
header_omitted: @inner_options[:header_omitted],
|
35
|
+
header_context: @inner_options[:header_context],
|
36
|
+
&block
|
37
|
+
)
|
38
|
+
inner_options = {
|
39
|
+
header_omitted: header_omitted,
|
40
|
+
header_context: header_context
|
41
|
+
}
|
42
|
+
|
43
|
+
to << BOM if bom
|
44
|
+
|
45
|
+
csv = ::CSV.new(to, **csv_options)
|
46
|
+
@writer.write(items, to: csv, **inner_options, &block)
|
53
47
|
end
|
54
48
|
end
|
55
49
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableStructure
|
4
|
+
module Schema
|
5
|
+
module ClassMethods
|
6
|
+
def +(schema)
|
7
|
+
self_schema = self
|
8
|
+
Class.new do
|
9
|
+
include ::TableStructure::Schema
|
10
|
+
|
11
|
+
@__column_definitions__ = [
|
12
|
+
self_schema.column_definitions,
|
13
|
+
schema.column_definitions
|
14
|
+
].flatten
|
15
|
+
|
16
|
+
@__context_builders__ =
|
17
|
+
{}
|
18
|
+
.merge!(self_schema.context_builders)
|
19
|
+
.merge!(schema.context_builders)
|
20
|
+
|
21
|
+
@__column_converters__ =
|
22
|
+
{}
|
23
|
+
.merge!(self_schema.column_converters)
|
24
|
+
.merge!(schema.column_converters)
|
25
|
+
|
26
|
+
@__result_builders__ =
|
27
|
+
{}
|
28
|
+
.merge!(self_schema.result_builders)
|
29
|
+
.merge!(schema.result_builders)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -6,19 +6,19 @@ module TableStructure
|
|
6
6
|
class Attrs
|
7
7
|
attr_reader :keys, :size
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
@name =
|
11
|
-
@keys = optimize_size([
|
12
|
-
@value =
|
13
|
-
@size =
|
9
|
+
def initialize(name:, key:, value:, size:)
|
10
|
+
@name = name
|
11
|
+
@keys = optimize_size([key].flatten, size)
|
12
|
+
@value = value
|
13
|
+
@size = size
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def names(context, table_context)
|
17
17
|
name = Utils.evaluate_callable(@name, context, table_context)
|
18
18
|
optimize_size(name, @size)
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
21
|
+
def values(context, table_context)
|
22
22
|
value = Utils.evaluate_callable(@value, context, table_context)
|
23
23
|
optimize_size(value, @size)
|
24
24
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableStructure
|
4
|
+
module Schema
|
5
|
+
module Column
|
6
|
+
module Definition
|
7
|
+
class Compiler
|
8
|
+
DEFAULT_ATTRS = {
|
9
|
+
name: nil,
|
10
|
+
key: nil,
|
11
|
+
value: nil,
|
12
|
+
size: nil,
|
13
|
+
omitted: false
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
DEFAULT_SIZE = 1
|
17
|
+
|
18
|
+
def initialize(name, definitions, options)
|
19
|
+
@name = name
|
20
|
+
@definitions = definitions
|
21
|
+
@options = options
|
22
|
+
end
|
23
|
+
|
24
|
+
def compile(context = nil)
|
25
|
+
@definitions
|
26
|
+
.map { |definition| Utils.evaluate_callable(definition, context) }
|
27
|
+
.map.with_index do |definition, i|
|
28
|
+
validator = Validator.new(@name, i)
|
29
|
+
|
30
|
+
[definition]
|
31
|
+
.flatten
|
32
|
+
.map do |definition|
|
33
|
+
if definition.is_a?(Hash)
|
34
|
+
definition = DEFAULT_ATTRS.merge(definition)
|
35
|
+
omitted = definition.delete(:omitted)
|
36
|
+
next if Utils.evaluate_callable(omitted, context)
|
37
|
+
|
38
|
+
definition = evaluate_attrs(definition, context)
|
39
|
+
validator.validate(**definition)
|
40
|
+
definition[:size] = determine_size(**definition)
|
41
|
+
definition
|
42
|
+
elsif Utils.schema_instance?(definition)
|
43
|
+
definition
|
44
|
+
elsif Utils.schema_class?(definition)
|
45
|
+
definition.new(context: context)
|
46
|
+
elsif definition.nil? && @options[:nil_definitions_ignored]
|
47
|
+
next
|
48
|
+
else
|
49
|
+
raise Error.new('Invalid definition.', @name, i)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
.flatten
|
54
|
+
.compact
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def evaluate_attrs(definition, context)
|
60
|
+
size = Utils.evaluate_callable(definition[:size], context)
|
61
|
+
definition.merge(size: size)
|
62
|
+
end
|
63
|
+
|
64
|
+
def determine_size(name:, key:, size:, **)
|
65
|
+
return size if size
|
66
|
+
|
67
|
+
[calculate_size(name), calculate_size(key)].max
|
68
|
+
end
|
69
|
+
|
70
|
+
def calculate_size(val)
|
71
|
+
if val.is_a?(Array)
|
72
|
+
return val.empty? ? DEFAULT_SIZE : val.size
|
73
|
+
end
|
74
|
+
|
75
|
+
DEFAULT_SIZE
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableStructure
|
4
|
+
module Schema
|
5
|
+
module Column
|
6
|
+
module Definition
|
7
|
+
class Error < ::TableStructure::Error
|
8
|
+
attr_reader :schema_name, :definition_index
|
9
|
+
|
10
|
+
def initialize(error_message, schema_name, definition_index)
|
11
|
+
@schema_name = schema_name
|
12
|
+
@definition_index = definition_index
|
13
|
+
super("#{error_message} [#{schema_name}] defined position of column(s): #{definition_index + 1}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableStructure
|
4
|
+
module Schema
|
5
|
+
module Column
|
6
|
+
module Definition
|
7
|
+
class Validator
|
8
|
+
DEFAULT_SIZE = 1
|
9
|
+
|
10
|
+
def initialize(name, index)
|
11
|
+
@name = name
|
12
|
+
@index = index
|
13
|
+
end
|
14
|
+
|
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
|
19
|
+
if !key && name.respond_to?(:call) && !size
|
20
|
+
raise Error.new('"size" must be defined, because column size cannot be determined.', @name, @index)
|
21
|
+
end
|
22
|
+
if size && size < DEFAULT_SIZE
|
23
|
+
raise Error.new('"size" must be positive.', @name, @index)
|
24
|
+
end
|
25
|
+
if key && size && [key].flatten.size < size
|
26
|
+
raise Error.new('"key" size must not be less than specified "size".', @name, @index)
|
27
|
+
end
|
28
|
+
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableStructure
|
4
|
+
module Schema
|
5
|
+
module Column
|
6
|
+
module Factory
|
7
|
+
def self.create(name, definitions, context, options)
|
8
|
+
Definition::Compiler
|
9
|
+
.new(name, definitions, options)
|
10
|
+
.compile(context)
|
11
|
+
.map do |definition|
|
12
|
+
if definition.is_a?(Hash)
|
13
|
+
Attrs.new(**definition)
|
14
|
+
else
|
15
|
+
Schema.new(definition)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -10,7 +10,7 @@ module TableStructure
|
|
10
10
|
@table = schema.create_table
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
13
|
+
def names(header_context, _table_context)
|
14
14
|
@table.header(context: header_context)
|
15
15
|
end
|
16
16
|
|
@@ -18,7 +18,7 @@ module TableStructure
|
|
18
18
|
@table.send(:keys)
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
21
|
+
def values(row_context, _table_context)
|
22
22
|
@table.row(context: row_context)
|
23
23
|
end
|
24
24
|
|
@@ -8,11 +8,12 @@ module TableStructure
|
|
8
8
|
@row_converters = select_converters_for(:row, converters)
|
9
9
|
end
|
10
10
|
|
11
|
-
def extend_methods_for(table)
|
11
|
+
def extend_methods_for(table, name_prefix:, name_suffix:)
|
12
12
|
table_context = table.instance_variable_get(:@context)
|
13
|
-
table_options = table.instance_variable_get(:@options)
|
14
13
|
|
15
|
-
header_converters = @header_converters.merge(
|
14
|
+
header_converters = @header_converters.merge(
|
15
|
+
optional_header_converters(name_prefix: name_prefix, name_suffix: name_suffix)
|
16
|
+
)
|
16
17
|
row_converters = @row_converters
|
17
18
|
|
18
19
|
methods = {}
|
@@ -37,15 +38,15 @@ module TableStructure
|
|
37
38
|
.to_h
|
38
39
|
end
|
39
40
|
|
40
|
-
def optional_header_converters(
|
41
|
+
def optional_header_converters(name_prefix:, name_suffix:)
|
41
42
|
converters = {}
|
42
|
-
if
|
43
|
+
if name_prefix
|
43
44
|
converters[:_prepend_prefix_] =
|
44
|
-
create_prefix_converter(
|
45
|
+
create_prefix_converter(name_prefix)
|
45
46
|
end
|
46
|
-
if
|
47
|
+
if name_suffix
|
47
48
|
converters[:_append_suffix_] =
|
48
|
-
create_suffix_converter(
|
49
|
+
create_suffix_converter(name_suffix)
|
49
50
|
end
|
50
51
|
|
51
52
|
converters
|
@@ -18,15 +18,14 @@ module TableStructure
|
|
18
18
|
@builders = builders
|
19
19
|
end
|
20
20
|
|
21
|
-
def extend_methods_for(table)
|
22
|
-
|
23
|
-
table_options = table.instance_variable_get(:@options)
|
24
|
-
table_keys = table.send(:keys)
|
25
|
-
|
26
|
-
builders = select_builders(table_options[:result_type])
|
21
|
+
def extend_methods_for(table, result_type: :array)
|
22
|
+
builders = select_builders(result_type)
|
27
23
|
|
28
24
|
methods = {}
|
29
25
|
unless builders.empty?
|
26
|
+
table_context = table.instance_variable_get(:@context)
|
27
|
+
table_keys = table.send(:keys)
|
28
|
+
|
30
29
|
methods[:header] = create_method(builders, table_keys, table_context)
|
31
30
|
methods[:row] = create_method(builders, table_keys, table_context)
|
32
31
|
end
|
@@ -3,10 +3,6 @@
|
|
3
3
|
module TableStructure
|
4
4
|
module Schema
|
5
5
|
class Table
|
6
|
-
DEFAULT_OPTIONS = {
|
7
|
-
result_type: :array
|
8
|
-
}.freeze
|
9
|
-
|
10
6
|
def initialize(
|
11
7
|
columns,
|
12
8
|
context,
|
@@ -14,15 +10,15 @@ module TableStructure
|
|
14
10
|
)
|
15
11
|
@columns = columns
|
16
12
|
@context = context
|
17
|
-
@options =
|
13
|
+
@options = options
|
18
14
|
end
|
19
15
|
|
20
16
|
def header(context: nil)
|
21
|
-
values(:
|
17
|
+
values(:names, context)
|
22
18
|
end
|
23
19
|
|
24
20
|
def row(context: nil)
|
25
|
-
values(:
|
21
|
+
values(:values, context)
|
26
22
|
end
|
27
23
|
|
28
24
|
def rows(items)
|
@@ -8,8 +8,20 @@ module TableStructure
|
|
8
8
|
klass.extend(DSL::ContextBuilder)
|
9
9
|
klass.extend(DSL::Option)
|
10
10
|
klass.extend(DSL::ResultBuilder)
|
11
|
+
klass.extend(ClassMethods)
|
11
12
|
end
|
12
13
|
|
14
|
+
Definition = Struct.new(
|
15
|
+
'Definition',
|
16
|
+
:name,
|
17
|
+
:columns,
|
18
|
+
:context_builders,
|
19
|
+
:column_converters,
|
20
|
+
:result_builders,
|
21
|
+
:context,
|
22
|
+
:options
|
23
|
+
)
|
24
|
+
|
13
25
|
def initialize(
|
14
26
|
name: self.class.name,
|
15
27
|
context: nil,
|
@@ -23,14 +35,10 @@ module TableStructure
|
|
23
35
|
unless deprecated_options.empty?
|
24
36
|
caller_location = caller_locations(1, 1)
|
25
37
|
deprecated_options.keys.each do |k|
|
26
|
-
warn "[TableStructure] Specify
|
38
|
+
warn "[TableStructure] Specify :#{k} option on the writer or the iterator. #{caller_location}"
|
27
39
|
end
|
28
40
|
end
|
29
41
|
|
30
|
-
column_definitions = [].concat(self.class.column_definitions)
|
31
|
-
context_builders = {}.merge!(self.class.context_builders)
|
32
|
-
column_converters = {}.merge!(self.class.column_converters)
|
33
|
-
result_builders = {}.merge!(self.class.result_builders)
|
34
42
|
options = {
|
35
43
|
name_prefix: name_prefix,
|
36
44
|
name_suffix: name_suffix,
|
@@ -39,10 +47,17 @@ module TableStructure
|
|
39
47
|
nil_definitions_ignored: nil_definitions_ignored
|
40
48
|
}.merge!(self.class.options).merge!(deprecated_options)
|
41
49
|
|
42
|
-
|
50
|
+
context_builders = ContextBuilders.new({}.merge!(self.class.context_builders))
|
51
|
+
column_converters = ColumnConverters.new({}.merge!(self.class.column_converters))
|
52
|
+
result_builders = ResultBuilders.new({}.merge!(self.class.result_builders))
|
53
|
+
|
54
|
+
context = context_builders.build_for_table(context)
|
55
|
+
columns = Column::Factory.create(name, self.class.column_definitions, context, options)
|
56
|
+
|
57
|
+
@_definition_ =
|
43
58
|
Definition.new(
|
44
59
|
name,
|
45
|
-
|
60
|
+
columns,
|
46
61
|
context_builders,
|
47
62
|
column_converters,
|
48
63
|
result_builders,
|
@@ -51,8 +66,36 @@ module TableStructure
|
|
51
66
|
)
|
52
67
|
end
|
53
68
|
|
54
|
-
|
55
|
-
|
69
|
+
# TODO: Specify options using keyword arguments.
|
70
|
+
def create_table(**options)
|
71
|
+
options = @_definition_.options.merge(options)
|
72
|
+
|
73
|
+
table = Table.new(
|
74
|
+
@_definition_.columns,
|
75
|
+
@_definition_.context,
|
76
|
+
options
|
77
|
+
)
|
78
|
+
|
79
|
+
@_definition_.context_builders.extend_methods_for(table)
|
80
|
+
|
81
|
+
column_converters_options = {
|
82
|
+
name_prefix: options[:name_prefix],
|
83
|
+
name_suffix: options[:name_suffix]
|
84
|
+
}
|
85
|
+
|
86
|
+
@_definition_.column_converters.extend_methods_for(table, **column_converters_options)
|
87
|
+
|
88
|
+
result_builders_options = {
|
89
|
+
result_type: options[:result_type]
|
90
|
+
}
|
91
|
+
|
92
|
+
@_definition_.result_builders.extend_methods_for(table, **result_builders_options)
|
93
|
+
|
94
|
+
if block_given?
|
95
|
+
yield table
|
96
|
+
else
|
97
|
+
table
|
98
|
+
end
|
56
99
|
end
|
57
100
|
end
|
58
101
|
end
|
data/lib/table_structure.rb
CHANGED
@@ -6,15 +6,12 @@ module TableStructure
|
|
6
6
|
require 'table_structure/version'
|
7
7
|
|
8
8
|
require 'table_structure/schema'
|
9
|
+
require 'table_structure/schema/class_methods'
|
9
10
|
require 'table_structure/schema/dsl/column_converter'
|
10
11
|
require 'table_structure/schema/dsl/column_definition'
|
11
12
|
require 'table_structure/schema/dsl/context_builder'
|
12
13
|
require 'table_structure/schema/dsl/option'
|
13
14
|
require 'table_structure/schema/dsl/result_builder'
|
14
|
-
require 'table_structure/schema/definition'
|
15
|
-
require 'table_structure/schema/definition/compiler'
|
16
|
-
require 'table_structure/schema/definition/error'
|
17
|
-
require 'table_structure/schema/definition/validator'
|
18
15
|
require 'table_structure/schema/column_converters'
|
19
16
|
require 'table_structure/schema/context_builders'
|
20
17
|
require 'table_structure/schema/result_builders'
|
@@ -22,6 +19,10 @@ module TableStructure
|
|
22
19
|
require 'table_structure/schema/table/key_decorator'
|
23
20
|
require 'table_structure/schema/column/attrs'
|
24
21
|
require 'table_structure/schema/column/schema'
|
22
|
+
require 'table_structure/schema/column/factory'
|
23
|
+
require 'table_structure/schema/column/definition/compiler'
|
24
|
+
require 'table_structure/schema/column/definition/error'
|
25
|
+
require 'table_structure/schema/column/definition/validator'
|
25
26
|
require 'table_structure/schema/utils'
|
26
27
|
require 'table_structure/writer'
|
27
28
|
require 'table_structure/csv/writer'
|
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.
|
4
|
+
version: 0.3.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jsmmr
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-12-
|
11
|
+
date: 2019-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -74,14 +74,15 @@ files:
|
|
74
74
|
- lib/table_structure/csv/writer.rb
|
75
75
|
- lib/table_structure/iterator.rb
|
76
76
|
- lib/table_structure/schema.rb
|
77
|
+
- lib/table_structure/schema/class_methods.rb
|
77
78
|
- lib/table_structure/schema/column/attrs.rb
|
79
|
+
- lib/table_structure/schema/column/definition/compiler.rb
|
80
|
+
- lib/table_structure/schema/column/definition/error.rb
|
81
|
+
- lib/table_structure/schema/column/definition/validator.rb
|
82
|
+
- lib/table_structure/schema/column/factory.rb
|
78
83
|
- lib/table_structure/schema/column/schema.rb
|
79
84
|
- lib/table_structure/schema/column_converters.rb
|
80
85
|
- lib/table_structure/schema/context_builders.rb
|
81
|
-
- lib/table_structure/schema/definition.rb
|
82
|
-
- lib/table_structure/schema/definition/compiler.rb
|
83
|
-
- lib/table_structure/schema/definition/error.rb
|
84
|
-
- lib/table_structure/schema/definition/validator.rb
|
85
86
|
- lib/table_structure/schema/dsl/column_converter.rb
|
86
87
|
- lib/table_structure/schema/dsl/column_definition.rb
|
87
88
|
- lib/table_structure/schema/dsl/context_builder.rb
|
@@ -113,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
114
|
- !ruby/object:Gem::Version
|
114
115
|
version: '0'
|
115
116
|
requirements: []
|
116
|
-
rubygems_version: 3.
|
117
|
+
rubygems_version: 3.1.2
|
117
118
|
signing_key:
|
118
119
|
specification_version: 4
|
119
120
|
summary: Generates and outputs table structured data.
|
@@ -1,79 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module TableStructure
|
4
|
-
module Schema
|
5
|
-
class Definition
|
6
|
-
class Compiler
|
7
|
-
DEFAULT_ATTRS = {
|
8
|
-
name: nil,
|
9
|
-
key: nil,
|
10
|
-
value: nil,
|
11
|
-
size: nil,
|
12
|
-
omitted: false
|
13
|
-
}.freeze
|
14
|
-
|
15
|
-
DEFAULT_SIZE = 1
|
16
|
-
|
17
|
-
def initialize(name, definitions, options)
|
18
|
-
@name = name
|
19
|
-
@definitions = definitions
|
20
|
-
@options = options
|
21
|
-
end
|
22
|
-
|
23
|
-
def compile(context = nil)
|
24
|
-
@definitions
|
25
|
-
.map { |definition| Utils.evaluate_callable(definition, context) }
|
26
|
-
.map.with_index do |definition, i|
|
27
|
-
validator = Validator.new(@name, i)
|
28
|
-
|
29
|
-
[definition]
|
30
|
-
.flatten
|
31
|
-
.map do |definition|
|
32
|
-
if definition.is_a?(Hash)
|
33
|
-
definition = DEFAULT_ATTRS.merge(definition)
|
34
|
-
omitted = definition.delete(:omitted)
|
35
|
-
next if Utils.evaluate_callable(omitted, context)
|
36
|
-
|
37
|
-
definition = evaluate_attrs(definition, context)
|
38
|
-
validator.validate(**definition)
|
39
|
-
definition[:size] = determine_size(**definition)
|
40
|
-
definition
|
41
|
-
elsif Utils.schema_instance?(definition)
|
42
|
-
definition
|
43
|
-
elsif Utils.schema_class?(definition)
|
44
|
-
definition.new(context: context)
|
45
|
-
elsif definition.nil? && @options[:nil_definitions_ignored]
|
46
|
-
next
|
47
|
-
else
|
48
|
-
raise Error.new('Invalid definition.', @name, i)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
.flatten
|
53
|
-
.compact
|
54
|
-
end
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def evaluate_attrs(definition, context)
|
59
|
-
size = Utils.evaluate_callable(definition[:size], context)
|
60
|
-
definition.merge(size: size)
|
61
|
-
end
|
62
|
-
|
63
|
-
def determine_size(name:, key:, size:, **)
|
64
|
-
return size if size
|
65
|
-
|
66
|
-
[calculate_size(name), calculate_size(key)].max
|
67
|
-
end
|
68
|
-
|
69
|
-
def calculate_size(val)
|
70
|
-
if val.is_a?(Array)
|
71
|
-
return val.empty? ? DEFAULT_SIZE : val.size
|
72
|
-
end
|
73
|
-
|
74
|
-
DEFAULT_SIZE
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module TableStructure
|
4
|
-
module Schema
|
5
|
-
class Definition
|
6
|
-
class Error < ::TableStructure::Error
|
7
|
-
attr_reader :schema_name, :definition_index
|
8
|
-
|
9
|
-
def initialize(error_message, schema_name, definition_index)
|
10
|
-
@schema_name = schema_name
|
11
|
-
@definition_index = definition_index
|
12
|
-
super("#{error_message} [#{schema_name}] defined position of column(s): #{definition_index + 1}")
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module TableStructure
|
4
|
-
module Schema
|
5
|
-
class Definition
|
6
|
-
class Validator
|
7
|
-
DEFAULT_SIZE = 1
|
8
|
-
|
9
|
-
def initialize(name, index)
|
10
|
-
@name = name
|
11
|
-
@index = index
|
12
|
-
end
|
13
|
-
|
14
|
-
def validate(name:, key:, size:, **)
|
15
|
-
if key.respond_to?(:call)
|
16
|
-
raise Error.new('"key" must not be lambda.', @name, @index)
|
17
|
-
end
|
18
|
-
if !key && name.respond_to?(:call) && !size
|
19
|
-
raise Error.new('"size" must be defined, because column size cannot be determined.', @name, @index)
|
20
|
-
end
|
21
|
-
if size && size < DEFAULT_SIZE
|
22
|
-
raise Error.new('"size" must be positive.', @name, @index)
|
23
|
-
end
|
24
|
-
if key && size && [key].flatten.size < size
|
25
|
-
raise Error.new('"key" size must not be less than specified "size".', @name, @index)
|
26
|
-
end
|
27
|
-
|
28
|
-
true
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module TableStructure
|
4
|
-
module Schema
|
5
|
-
class Definition
|
6
|
-
attr_reader :options
|
7
|
-
|
8
|
-
def initialize(
|
9
|
-
name,
|
10
|
-
column_definitions,
|
11
|
-
context_builders,
|
12
|
-
column_converters,
|
13
|
-
result_builders,
|
14
|
-
context,
|
15
|
-
options
|
16
|
-
)
|
17
|
-
@name = name
|
18
|
-
@context_builders = ContextBuilders.new(context_builders)
|
19
|
-
@column_converters = ColumnConverters.new(column_converters)
|
20
|
-
@result_builders = ResultBuilders.new(result_builders)
|
21
|
-
@context = @context_builders.build_for_table(context)
|
22
|
-
@options = options
|
23
|
-
|
24
|
-
@columns = create_columns(@name, column_definitions, @context, @options)
|
25
|
-
end
|
26
|
-
|
27
|
-
def create_table(**options)
|
28
|
-
options = @options.merge(options)
|
29
|
-
|
30
|
-
table = Table.new(
|
31
|
-
@columns,
|
32
|
-
@context,
|
33
|
-
options
|
34
|
-
)
|
35
|
-
|
36
|
-
@context_builders.extend_methods_for(table)
|
37
|
-
@column_converters.extend_methods_for(table)
|
38
|
-
@result_builders.extend_methods_for(table)
|
39
|
-
|
40
|
-
if block_given?
|
41
|
-
yield table
|
42
|
-
else
|
43
|
-
table
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def create_columns(name, definitions, context, options)
|
50
|
-
Compiler
|
51
|
-
.new(name, definitions, options)
|
52
|
-
.compile(context)
|
53
|
-
.map do |definition|
|
54
|
-
if definition.is_a?(Hash)
|
55
|
-
Column::Attrs.new(definition)
|
56
|
-
else
|
57
|
-
Column::Schema.new(definition)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|