table_structure 0.2.2 → 0.3.0
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/CHANGELOG.md +15 -0
- data/Gemfile.lock +1 -1
- data/README.md +76 -2
- data/lib/table_structure.rb +2 -0
- data/lib/table_structure/schema.rb +7 -3
- data/lib/table_structure/schema/column.rb +5 -37
- data/lib/table_structure/schema/column/attrs.rb +63 -0
- data/lib/table_structure/schema/column/schema.rb +29 -0
- data/lib/table_structure/schema/definition.rb +17 -5
- data/lib/table_structure/schema/table.rb +8 -7
- data/lib/table_structure/schema/utils.rb +18 -0
- data/lib/table_structure/version.rb +1 -1
- data/table_structure.gemspec +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e966906136bf9907efb5bf1ad6fc1f2181ded3e4c6bbd6f76c19750198ecadd
|
4
|
+
data.tar.gz: c1f11210056449c18d31c693e758872659d603b1e9d1a28013cf0e35baff8776
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a98e9de6bd5d32941ee5730a8f34294d459a04ebc5e22a547887225421f7f91fcbbce59b026c6af5a96fa6215e38f0be6ed03b6155e04365418576f982d15873
|
7
|
+
data.tar.gz: b57d5dc8e40f0937ea14c06c08fb54d720bbd3215186b9357fc0f9f28705ea905938f238157999ecb62221ae84fd41d28872c63653557919b2be92a2162bc468
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# 0.3.0
|
2
|
+
Changes:
|
3
|
+
- `TableStructure::Schema`
|
4
|
+
- Add `:omitted` key for column(s) DSL.
|
5
|
+
- Support nested schema.
|
6
|
+
- Add following options for schema initialization:
|
7
|
+
- `:key_prefix`
|
8
|
+
- `:key_suffix`
|
9
|
+
|
10
|
+
# 0.2.0
|
11
|
+
Changes:
|
12
|
+
- Add `TableStructure::Iterator`.
|
13
|
+
|
14
|
+
# 0.1.0
|
15
|
+
- First version
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
[](https://travis-ci.org/jsmmr/ruby_table_structure)
|
4
4
|
|
5
5
|
`TableStructure` has two major functions.
|
6
|
-
The functions are `TableStructure::Schema` that defines the schema of a table using DSL and ` TableStructure::Writer` that converts and outputs data with
|
6
|
+
The functions are `TableStructure::Schema` that defines the schema of a table using DSL and ` TableStructure::Writer` that converts and outputs data with the schema.
|
7
7
|
|
8
8
|
## Installation
|
9
9
|
|
@@ -132,6 +132,10 @@ class SampleTableSchema
|
|
132
132
|
end
|
133
133
|
}
|
134
134
|
|
135
|
+
## When nesting schemas, same key must not exist in parent and child schemas.
|
136
|
+
## This can also be avoided by specifying :key_prefix or :key_suffix option.
|
137
|
+
# columns ->(table) { NestedSchema.new(context: table, key_prefix: 'foo_', key_suffix: '_bar') }
|
138
|
+
|
135
139
|
column_converter :to_s, ->(val, *) { val.to_s }
|
136
140
|
end
|
137
141
|
|
@@ -180,6 +184,76 @@ enum.lazy.select { |item| item[:q1] == '⭕️' }.take(1).force
|
|
180
184
|
|
181
185
|
### Advanced
|
182
186
|
|
187
|
+
You can also omit columns by defining `:omitted`.
|
188
|
+
```ruby
|
189
|
+
class SampleTableSchema
|
190
|
+
include TableStructure::Schema
|
191
|
+
|
192
|
+
column name: 'ID',
|
193
|
+
value: ->(row, _table) { row[:id] }
|
194
|
+
|
195
|
+
column name: 'Name',
|
196
|
+
value: ->(row, *) { row[:name] }
|
197
|
+
|
198
|
+
column name: 'Secret',
|
199
|
+
value: ->(row, *) { row[:secret] },
|
200
|
+
omitted: ->(table) { !table[:admin] }
|
201
|
+
end
|
202
|
+
|
203
|
+
context = { admin: true }
|
204
|
+
|
205
|
+
schema = SampleTableSchema.new(context: context)
|
206
|
+
```
|
207
|
+
|
208
|
+
You can also nest schemas.
|
209
|
+
```ruby
|
210
|
+
class PetsSchema
|
211
|
+
include TableStructure::Schema
|
212
|
+
|
213
|
+
columns name: ['Pet 1', 'Pet 2', 'Pet 3'],
|
214
|
+
value: ->(row, *) { row[:pets] }
|
215
|
+
end
|
216
|
+
|
217
|
+
class QuestionsSchema
|
218
|
+
include TableStructure::Schema
|
219
|
+
|
220
|
+
columns ->(table) {
|
221
|
+
table[:questions].map do |question|
|
222
|
+
{
|
223
|
+
name: question[:id],
|
224
|
+
value: ->(row, *) { row[:answers][question[:id]] }
|
225
|
+
}
|
226
|
+
end
|
227
|
+
}
|
228
|
+
end
|
229
|
+
|
230
|
+
class SampleTableSchema
|
231
|
+
include TableStructure::Schema
|
232
|
+
|
233
|
+
column name: 'ID',
|
234
|
+
value: ->(row, _table) { row[:id] }
|
235
|
+
|
236
|
+
column name: 'Name',
|
237
|
+
value: ->(row, *) { row[:name] }
|
238
|
+
|
239
|
+
columns ->(table) { PetsSchema.new(context: table) }
|
240
|
+
|
241
|
+
columns ->(table) { QuestionsSchema.new(context: table) }
|
242
|
+
|
243
|
+
column_converter :to_s, ->(val, *) { val.to_s }
|
244
|
+
end
|
245
|
+
|
246
|
+
context = {
|
247
|
+
questions: [
|
248
|
+
{ id: 'Q1', text: 'Do you like sushi?' },
|
249
|
+
{ id: 'Q2', text: 'Do you like yakiniku?' },
|
250
|
+
{ id: 'Q3', text: 'Do you like ramen?' }
|
251
|
+
]
|
252
|
+
}
|
253
|
+
|
254
|
+
schema = SampleTableSchema.new(context: context)
|
255
|
+
```
|
256
|
+
|
183
257
|
You can also use `context_builder`.
|
184
258
|
This may be useful when `column` definition lambda is complicated.
|
185
259
|
```ruby
|
@@ -219,7 +293,7 @@ class SampleTableSchema
|
|
219
293
|
end
|
220
294
|
```
|
221
295
|
|
222
|
-
If you want to convert CSV character code,
|
296
|
+
If you want to convert CSV character code, you do so within block of `write` method.
|
223
297
|
```ruby
|
224
298
|
File.open('sample.csv', 'w') do |f|
|
225
299
|
writer.write(items, to: CSV.new(f)) do |row_values|
|
data/lib/table_structure.rb
CHANGED
@@ -16,6 +16,8 @@ module TableStructure
|
|
16
16
|
require 'table_structure/schema/definition/validator'
|
17
17
|
require 'table_structure/schema/table'
|
18
18
|
require 'table_structure/schema/column'
|
19
|
+
require 'table_structure/schema/column/attrs'
|
20
|
+
require 'table_structure/schema/column/schema'
|
19
21
|
require 'table_structure/schema/utils'
|
20
22
|
require 'table_structure/writer'
|
21
23
|
require 'table_structure/iterator'
|
@@ -10,7 +10,11 @@ module TableStructure
|
|
10
10
|
klass.extend(DSL::ResultBuilder)
|
11
11
|
end
|
12
12
|
|
13
|
-
DEFAULT_OPTIONS = {
|
13
|
+
DEFAULT_OPTIONS = {
|
14
|
+
result_type: :array,
|
15
|
+
key_prefix: nil,
|
16
|
+
key_suffix: nil
|
17
|
+
}.freeze
|
14
18
|
|
15
19
|
def initialize(context: nil, **options)
|
16
20
|
column_definitions = self.class.column_definitions
|
@@ -30,12 +34,12 @@ module TableStructure
|
|
30
34
|
|
31
35
|
def header(context: nil)
|
32
36
|
context = self.class.context_builders[:header].call(context)
|
33
|
-
@table_structure_schema_table_.
|
37
|
+
@table_structure_schema_table_.header_values(context)
|
34
38
|
end
|
35
39
|
|
36
40
|
def row(context: nil)
|
37
41
|
context = self.class.context_builders[:row].call(context)
|
38
|
-
@table_structure_schema_table_.
|
42
|
+
@table_structure_schema_table_.row_values(context)
|
39
43
|
end
|
40
44
|
|
41
45
|
def column_converters
|
@@ -2,44 +2,12 @@
|
|
2
2
|
|
3
3
|
module TableStructure
|
4
4
|
module Schema
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@name = name
|
10
|
-
@key = key
|
11
|
-
@value = value
|
12
|
-
@size = size
|
13
|
-
end
|
14
|
-
|
15
|
-
def name(header_context, table_context)
|
16
|
-
name = Utils.evaluate_callable(@name, header_context, table_context)
|
17
|
-
optimize_size(name)
|
18
|
-
end
|
19
|
-
|
20
|
-
def key
|
21
|
-
optimize_size(@key)
|
22
|
-
end
|
23
|
-
|
24
|
-
def value(row_context, table_context)
|
25
|
-
value = Utils.evaluate_callable(@value, row_context, table_context)
|
26
|
-
optimize_size(value)
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def optimize_size(value)
|
32
|
-
return value if @size == 1 && !value.is_a?(Array)
|
33
|
-
|
34
|
-
values = [value].flatten
|
35
|
-
actual_size = values.size
|
36
|
-
expected_size = @size
|
37
|
-
if actual_size > expected_size
|
38
|
-
values[0, expected_size]
|
39
|
-
elsif actual_size < expected_size
|
40
|
-
[].concat(values).fill(nil, actual_size, (expected_size - actual_size))
|
5
|
+
module Column
|
6
|
+
def self.create(definition, options)
|
7
|
+
if definition.is_a?(Hash)
|
8
|
+
Attrs.new(definition, options)
|
41
9
|
else
|
42
|
-
|
10
|
+
Schema.new(definition)
|
43
11
|
end
|
44
12
|
end
|
45
13
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableStructure
|
4
|
+
module Schema
|
5
|
+
module Column
|
6
|
+
class Attrs
|
7
|
+
attr_reader :name, :key, :vlaue, :size
|
8
|
+
|
9
|
+
def initialize(definition, options)
|
10
|
+
@name = definition[:name]
|
11
|
+
@key = definition[:key]
|
12
|
+
@value = definition[:value]
|
13
|
+
@size = definition[:size]
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def name(header_context, table_context)
|
18
|
+
name = Utils.evaluate_callable(@name, header_context, table_context)
|
19
|
+
optimize_size(name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def key
|
23
|
+
key = optimize_size(@key)
|
24
|
+
decorate_key(key)
|
25
|
+
end
|
26
|
+
|
27
|
+
def value(row_context, table_context)
|
28
|
+
value = Utils.evaluate_callable(@value, row_context, table_context)
|
29
|
+
optimize_size(value)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def optimize_size(value)
|
35
|
+
return value if @size == 1 && !value.is_a?(Array)
|
36
|
+
|
37
|
+
values = [value].flatten
|
38
|
+
actual_size = values.size
|
39
|
+
expected_size = @size
|
40
|
+
if actual_size > expected_size
|
41
|
+
values[0, expected_size]
|
42
|
+
elsif actual_size < expected_size
|
43
|
+
[].concat(values).fill(nil, actual_size, (expected_size - actual_size))
|
44
|
+
else
|
45
|
+
values
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def decorate_key(key)
|
50
|
+
return key unless @options[:key_prefix] || @options[:key_suffix]
|
51
|
+
|
52
|
+
[key].flatten.map do |key|
|
53
|
+
next key unless key
|
54
|
+
|
55
|
+
decorated_key = "#{@options[:key_prefix]}#{key}#{@options[:key_suffix]}"
|
56
|
+
decorated_key = decorated_key.to_sym if key.is_a?(Symbol)
|
57
|
+
decorated_key
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TableStructure
|
4
|
+
module Schema
|
5
|
+
module Column
|
6
|
+
class Schema
|
7
|
+
attr_reader :schema
|
8
|
+
|
9
|
+
def initialize(schema)
|
10
|
+
@schema = schema
|
11
|
+
end
|
12
|
+
|
13
|
+
def name(header_context, _table_context)
|
14
|
+
schema.header(context: header_context)
|
15
|
+
end
|
16
|
+
|
17
|
+
def key
|
18
|
+
schema
|
19
|
+
.instance_variable_get(:@table_structure_schema_table_)
|
20
|
+
.send(:keys)
|
21
|
+
end
|
22
|
+
|
23
|
+
def value(row_context, _table_context)
|
24
|
+
schema.row(context: row_context)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -7,7 +7,8 @@ module TableStructure
|
|
7
7
|
name: nil,
|
8
8
|
key: nil,
|
9
9
|
value: nil,
|
10
|
-
size: nil
|
10
|
+
size: nil,
|
11
|
+
omitted: false
|
11
12
|
}.freeze
|
12
13
|
|
13
14
|
DEFAULT_SIZE = 1
|
@@ -26,13 +27,24 @@ module TableStructure
|
|
26
27
|
[definition]
|
27
28
|
.flatten
|
28
29
|
.map do |definition|
|
29
|
-
definition
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
if definition.is_a?(Hash)
|
31
|
+
definition = DEFAULT_ATTRS.merge(definition)
|
32
|
+
omitted = definition.delete(:omitted)
|
33
|
+
next if Utils.evaluate_callable(omitted, context)
|
34
|
+
|
35
|
+
validator.validate(definition)
|
36
|
+
definition[:size] = determine_size(definition)
|
37
|
+
definition
|
38
|
+
elsif Utils.schema_instance?(definition)
|
39
|
+
definition
|
40
|
+
# elsif Utils.schema_class?(definition)
|
41
|
+
# # TODO: This doesn't work as expected when result_type: :hash is specified.
|
42
|
+
# definition.new(context: context, **@options)
|
43
|
+
end
|
33
44
|
end
|
34
45
|
end
|
35
46
|
.flatten
|
47
|
+
.compact
|
36
48
|
end
|
37
49
|
|
38
50
|
private
|
@@ -10,23 +10,28 @@ module TableStructure
|
|
10
10
|
@column_converters = default_column_converters.merge(column_converters)
|
11
11
|
@result_builders = default_result_builders(options).merge(result_builders)
|
12
12
|
@context = context
|
13
|
+
@options = options
|
13
14
|
end
|
14
15
|
|
15
|
-
def
|
16
|
+
def header_values(context)
|
16
17
|
values(:name, context)
|
17
18
|
end
|
18
19
|
|
19
|
-
def
|
20
|
+
def row_values(context)
|
20
21
|
values(:value, context)
|
21
22
|
end
|
22
23
|
|
24
|
+
def keys
|
25
|
+
@columns.map(&:key).flatten
|
26
|
+
end
|
27
|
+
|
23
28
|
private
|
24
29
|
|
25
30
|
def build_columns(definitions, context, options)
|
26
31
|
Definition
|
27
32
|
.new(definitions, options)
|
28
33
|
.compile(context)
|
29
|
-
.map { |
|
34
|
+
.map { |definition| Column.create(definition, options) }
|
30
35
|
end
|
31
36
|
|
32
37
|
def default_column_converters
|
@@ -41,10 +46,6 @@ module TableStructure
|
|
41
46
|
result_builders
|
42
47
|
end
|
43
48
|
|
44
|
-
def keys
|
45
|
-
@columns.map(&:key).flatten
|
46
|
-
end
|
47
|
-
|
48
49
|
def values(method, context)
|
49
50
|
columns = @columns
|
50
51
|
.map { |column| column.send(method, context, @context) }
|
@@ -6,6 +6,24 @@ module TableStructure
|
|
6
6
|
def self.evaluate_callable(val, *params)
|
7
7
|
val.respond_to?(:call) ? val.call(*params) : val
|
8
8
|
end
|
9
|
+
|
10
|
+
def self.concat_key(key, prefix, suffix)
|
11
|
+
case key
|
12
|
+
when Symbol
|
13
|
+
"#{prefix}#{key}#{suffix}".to_sym
|
14
|
+
else
|
15
|
+
"#{prefix}#{key}#{suffix}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.schema_class?(val)
|
20
|
+
val.is_a?(Class) &&
|
21
|
+
val.included_modules.include?(::TableStructure::Schema)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.schema_instance?(val)
|
25
|
+
val.is_a?(::TableStructure::Schema)
|
26
|
+
end
|
9
27
|
end
|
10
28
|
end
|
11
29
|
end
|
data/table_structure.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.email = ['jsmmr@icloud.com']
|
12
12
|
|
13
13
|
spec.summary = 'Creates and outputs table structured data.'
|
14
|
-
spec.description = '
|
14
|
+
spec.description = 'Creates and outputs table structured data. Useful for creating CSV.'
|
15
15
|
spec.homepage = 'https://github.com/jsmmr/ruby_table_structure'
|
16
16
|
spec.license = 'MIT'
|
17
17
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: table_structure
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jsmmr
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-08-
|
11
|
+
date: 2019-08-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,8 +52,7 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
-
description:
|
56
|
-
CSV.
|
55
|
+
description: Creates and outputs table structured data. Useful for creating CSV.
|
57
56
|
email:
|
58
57
|
- jsmmr@icloud.com
|
59
58
|
executables: []
|
@@ -63,6 +62,7 @@ files:
|
|
63
62
|
- ".gitignore"
|
64
63
|
- ".rspec"
|
65
64
|
- ".travis.yml"
|
65
|
+
- CHANGELOG.md
|
66
66
|
- Gemfile
|
67
67
|
- Gemfile.lock
|
68
68
|
- LICENSE.txt
|
@@ -74,6 +74,8 @@ files:
|
|
74
74
|
- lib/table_structure/iterator.rb
|
75
75
|
- lib/table_structure/schema.rb
|
76
76
|
- lib/table_structure/schema/column.rb
|
77
|
+
- lib/table_structure/schema/column/attrs.rb
|
78
|
+
- lib/table_structure/schema/column/schema.rb
|
77
79
|
- lib/table_structure/schema/definition.rb
|
78
80
|
- lib/table_structure/schema/definition/error.rb
|
79
81
|
- lib/table_structure/schema/definition/validator.rb
|