exwiw 0.9.7 → 0.9.9
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 +16 -0
- data/README.md +90 -27
- data/lib/exwiw/adapter/mongodb_adapter.rb +118 -2
- data/lib/exwiw/determine_table_processing_order.rb +109 -31
- data/lib/exwiw/explain_runner.rb +13 -2
- data/lib/exwiw/japanese_names.rb +315 -0
- data/lib/exwiw/mongodb_collection_config.rb +31 -0
- data/lib/exwiw/row_transformer.rb +161 -19
- data/lib/exwiw/runner.rb +27 -4
- data/lib/exwiw/strict_keys.rb +79 -0
- data/lib/exwiw/table_config.rb +12 -2
- data/lib/exwiw/version.rb +1 -1
- data/lib/exwiw.rb +1 -0
- metadata +3 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Exwiw
|
|
4
|
+
# Raised when a schema config JSON carries a key that no declared attribute
|
|
5
|
+
# accepts. A subclass of ArgumentError so existing "invalid config" handling
|
|
6
|
+
# (and specs) that rescue ArgumentError keep working.
|
|
7
|
+
UnknownConfigKeyError = Class.new(ArgumentError)
|
|
8
|
+
|
|
9
|
+
# Strict key validation for the schema config JSON.
|
|
10
|
+
#
|
|
11
|
+
# Serdes deserialization is lenient: a key that matches no declared attribute
|
|
12
|
+
# is silently dropped. For hand-maintained schema configs that turns a typo
|
|
13
|
+
# (`reverse_scop`) — or a key another adapter supports but this one does not
|
|
14
|
+
# (`raw_sql` on a MongoDB field) — into a silent no-op: the config loads, the
|
|
15
|
+
# dump runs, and the requested masking/scoping simply never happens.
|
|
16
|
+
# {TableConfig.from} / {MongodbCollectionConfig.from} therefore validate the
|
|
17
|
+
# raw hash against the declared attributes before deserializing, recursing
|
|
18
|
+
# into nested config objects (belongs_tos, columns/fields, reverse_scope,
|
|
19
|
+
# embedded_in, replace_with_fake_data).
|
|
20
|
+
#
|
|
21
|
+
# Free-form annotations do not need an escape hatch: `comment` is a declared
|
|
22
|
+
# (documentation-only) attribute on the table/collection configs and their
|
|
23
|
+
# belongs_to/column/field entries, so it always passes.
|
|
24
|
+
module StrictKeys
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
# Validate `hash` (a raw config hash, string- or symbol-keyed) against the
|
|
28
|
+
# attributes declared on the Serdes class `klass`, recursing into nested
|
|
29
|
+
# Serdes-typed attributes. `owner` names the config for the error message
|
|
30
|
+
# (e.g. "table 'users'"); `path` locates a nested entry within it (e.g.
|
|
31
|
+
# "belongs_tos[0]"). No-op for non-Hash input — Serdes' own type errors
|
|
32
|
+
# cover malformed values.
|
|
33
|
+
def validate!(klass, hash, owner:, path: nil)
|
|
34
|
+
return unless hash.is_a?(Hash)
|
|
35
|
+
|
|
36
|
+
attributes = klass.__send__(:_serde_attrs)
|
|
37
|
+
rename_strategy = klass.__send__(:_serde_rename_strategy)
|
|
38
|
+
allowed = attributes.each_value.to_h { |attr| [attr.serialized_name(rename_strategy), attr] }
|
|
39
|
+
|
|
40
|
+
unknown = hash.keys.map(&:to_s) - allowed.keys
|
|
41
|
+
unless unknown.empty?
|
|
42
|
+
location = path ? " (in #{path})" : ""
|
|
43
|
+
raise UnknownConfigKeyError,
|
|
44
|
+
"Unknown key#{'s' if unknown.size > 1} #{unknown.map { |key| "'#{key}'" }.join(', ')} " \
|
|
45
|
+
"in #{owner}#{location}. Allowed keys: #{allowed.keys.sort.join(', ')}. " \
|
|
46
|
+
"Unknown keys are rejected because they would otherwise be silently ignored " \
|
|
47
|
+
"(hiding typos and keys this adapter does not support); remove or rename the key, " \
|
|
48
|
+
"or use `comment` for free-form notes."
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
allowed.each do |key, attr|
|
|
52
|
+
nested_klass = serdes_class(attr.attr_type)
|
|
53
|
+
next if nested_klass.nil?
|
|
54
|
+
|
|
55
|
+
value = hash.key?(key) ? hash[key] : hash[key.to_sym]
|
|
56
|
+
nested_path = path ? "#{path}.#{key}" : key
|
|
57
|
+
case value
|
|
58
|
+
when Hash
|
|
59
|
+
validate!(nested_klass, value, owner: owner, path: nested_path)
|
|
60
|
+
when Array
|
|
61
|
+
value.each_with_index do |element, index|
|
|
62
|
+
validate!(nested_klass, element, owner: owner, path: "#{nested_path}[#{index}]") if element.is_a?(Hash)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# The Serdes-including class a (possibly optional/array-wrapped) attribute
|
|
69
|
+
# type deserializes into, or nil for scalar types.
|
|
70
|
+
def serdes_class(type)
|
|
71
|
+
case type
|
|
72
|
+
when Serdes::OptionalType then serdes_class(type.base_type)
|
|
73
|
+
when Serdes::ArrayType then serdes_class(type.element_type)
|
|
74
|
+
when Serdes::ConcreteType then serdes_class(type.exact_type)
|
|
75
|
+
when Class then type if type.include?(Serdes)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
data/lib/exwiw/table_config.rb
CHANGED
|
@@ -48,6 +48,15 @@ module Exwiw
|
|
|
48
48
|
attribute :reverse_scope, Serdes::OptionalType.new(ReverseScope), skip_serializing_if_nil: true
|
|
49
49
|
|
|
50
50
|
def self.from(hash)
|
|
51
|
+
# Reject unknown keys before deserializing: Serdes silently drops them,
|
|
52
|
+
# which would turn a typo'd or unsupported key into a silent no-op (see
|
|
53
|
+
# Exwiw::StrictKeys). `comment` is a declared attribute here and on the
|
|
54
|
+
# nested belongs_to/column entries, so free-form notes stay accepted.
|
|
55
|
+
if hash.is_a?(Hash)
|
|
56
|
+
table_name = hash["name"] || hash[:name]
|
|
57
|
+
StrictKeys.validate!(self, hash, owner: "table '#{table_name}'")
|
|
58
|
+
end
|
|
59
|
+
|
|
51
60
|
config = super
|
|
52
61
|
config.send(:validate_after_load!)
|
|
53
62
|
config
|
|
@@ -244,10 +253,11 @@ module Exwiw
|
|
|
244
253
|
fake_data = column.replace_with_fake_data
|
|
245
254
|
return unless fake_data
|
|
246
255
|
|
|
247
|
-
|
|
256
|
+
supported_types = RowTransformer::PERSON_TYPES.keys + RowTransformer::FAKE_TYPES.keys
|
|
257
|
+
unless supported_types.include?(fake_data.type)
|
|
248
258
|
raise ArgumentError,
|
|
249
259
|
"Table '#{name}' column '#{column.name}': unknown replace_with_fake_data type '#{fake_data.type}' " \
|
|
250
|
-
"(supported: #{
|
|
260
|
+
"(supported: #{supported_types.join(', ')})."
|
|
251
261
|
end
|
|
252
262
|
|
|
253
263
|
seed_column = fake_data.seed.delete_prefix("#{name}.")
|
data/lib/exwiw/version.rb
CHANGED
data/lib/exwiw.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: exwiw
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shia
|
|
@@ -71,6 +71,7 @@ files:
|
|
|
71
71
|
- lib/exwiw/explain_runner.rb
|
|
72
72
|
- lib/exwiw/ext_json.rb
|
|
73
73
|
- lib/exwiw/fake_data.rb
|
|
74
|
+
- lib/exwiw/japanese_names.rb
|
|
74
75
|
- lib/exwiw/mongo_query.rb
|
|
75
76
|
- lib/exwiw/mongodb_collection_config.rb
|
|
76
77
|
- lib/exwiw/mongodb_field.rb
|
|
@@ -84,6 +85,7 @@ files:
|
|
|
84
85
|
- lib/exwiw/row_transformer.rb
|
|
85
86
|
- lib/exwiw/runner.rb
|
|
86
87
|
- lib/exwiw/schema_generator.rb
|
|
88
|
+
- lib/exwiw/strict_keys.rb
|
|
87
89
|
- lib/exwiw/table_column.rb
|
|
88
90
|
- lib/exwiw/table_config.rb
|
|
89
91
|
- lib/exwiw/version.rb
|