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