-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Flatten shapes when fetching with Model.shape #269
Changes from all commits
6020bf7
95e9676
9dda7f0
42dfef3
83f2273
50300ce
cccdae5
59972b9
02181ef
667fa2e
a5ad845
130d785
e60ed3c
04d4ac0
21a97ab
d155566
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative 'model/flattener' | ||
require_relative 'model/rbs' | ||
require_relative 'model/shape' | ||
require_relative 'model/operation_parser' | ||
|
@@ -37,15 +38,15 @@ module Model | |
}.freeze | ||
|
||
# @param [Hash] model Model | ||
# @param [String] target Target shape | ||
# @return [Hash] The shape | ||
def self.shape(model, target) | ||
if model['shapes'].key?(target) | ||
model['shapes'][target] | ||
elsif PRELUDE_SHAPES.key?(target) | ||
PRELUDE_SHAPES[target] | ||
# @param [String] id Shape ID | ||
# @return [Hash] | ||
def self.shape(model, id) | ||
if model['shapes'].key?(id) | ||
Flattener.new(model).shape(id) | ||
elsif PRELUDE_SHAPES.key?(id) | ||
PRELUDE_SHAPES[id] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any possible interaction between mixins and prelude shapes? I don't think there would be, but just want to check. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think so but I'll check. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried this: |
||
else | ||
raise ArgumentError, "Shape not found: #{target}" | ||
raise ArgumentError, "Shape not found: #{id}" | ||
end | ||
end | ||
end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# frozen_string_literal: true | ||
|
||
module Smithy | ||
module Model | ||
jterapin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# @api private | ||
class Flattener | ||
def initialize(model) | ||
@model = model | ||
end | ||
|
||
def shape(id) | ||
shape = @model['shapes'][id] | ||
return shape unless shape['mixins'] | ||
|
||
shape['mixins'].reverse_each do |mixin| | ||
mixin_shape = shape(mixin['target']) | ||
shape = deep_merge(mixin_shape, shape, exclude_traits(mixin_shape)) | ||
apply_traits(id, shape) | ||
shape.delete('mixins') | ||
mullermp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end | ||
|
||
shape | ||
end | ||
|
||
private | ||
|
||
def exclude_traits(shape) | ||
[ | ||
'smithy.api#mixin', | ||
*shape.fetch('traits', {}).fetch('smithy.api#mixin', {}).fetch('localTraits', []) | ||
] | ||
end | ||
|
||
def deep_merge(hash1, hash2, exclude_traits = [], context = nil) | ||
hash1 = hash1.dup | ||
if hash1['traits'] | ||
hash1['traits'] = hash1['traits'].except(*exclude_traits) | ||
hash1.delete('traits') if hash1['traits'].empty? | ||
end | ||
deep_merge!(hash1, hash2, exclude_traits, context) | ||
end | ||
|
||
def deep_merge!(hash1, hash2, exclude_traits, context) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The difference between deep_merge and deep_merge! is confusing - maybe these could use comments? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One returns a new hash and the other modifies the current hash, just like merge and merge!. I got inspiration from the rails implementations. I can add that here. |
||
hash1.merge!(hash2) do |key, v1, v2| | ||
if v1.is_a?(Hash) && v2.is_a?(Hash) | ||
deep_merge(v1, v2, exclude_traits, key) | ||
elsif v1.is_a?(Array) && v2.is_a?(Array) && context != 'traits' | ||
# Merge arrays, but only if the key is not a trait | ||
v1 + v2 | ||
else | ||
v2 | ||
end | ||
end | ||
end | ||
|
||
def apply_traits(id, shape) | ||
case shape['type'] | ||
when 'structure' | ||
structure(id, shape) | ||
when 'union' | ||
union(id, shape) | ||
when 'list' | ||
list(id, shape) | ||
when 'map' | ||
map_key(id, shape) | ||
map_value(id, shape) | ||
end | ||
end | ||
|
||
def structure(id, shape) | ||
shape['members'].each do |member_name, member_shape| | ||
member_id = "#{id}$#{member_name}" | ||
next unless apply_shape_exists?(member_id) | ||
|
||
apply_shape = shape(member_id) | ||
member_keys = shape['members'][member_name].keys | ||
shape['members'][member_name] = deep_merge(member_shape, apply_shape).slice(*member_keys) | ||
end | ||
end | ||
alias union structure | ||
|
||
def list(id, shape) | ||
member_id = "#{id}$member" | ||
return unless apply_shape_exists?(member_id) | ||
|
||
apply_shape = shape(member_id) | ||
member_keys = shape['member'].keys | ||
shape['member'] = deep_merge(shape['member'], apply_shape).slice(*member_keys) | ||
end | ||
|
||
def map_key(id, shape) | ||
key_id = "#{id}$key" | ||
return unless apply_shape_exists?(key_id) | ||
|
||
key_shape = shape(key_id) | ||
key_keys = shape['key'].keys | ||
shape['key'] = deep_merge(shape['key'], key_shape).slice(*key_keys) | ||
end | ||
|
||
def map_value(id, shape) | ||
value_id = "#{id}$value" | ||
return unless apply_shape_exists?(value_id) | ||
|
||
value_shape = shape(value_id) | ||
value_keys = shape['value'].keys | ||
shape['value'] = deep_merge(shape['value'], value_shape).slice(*value_keys) | ||
end | ||
|
||
def apply_shape_exists?(id) | ||
@model['shapes'][id] && @model['shapes'][id]['type'] == 'apply' | ||
end | ||
end | ||
end | ||
end |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would there ever be multiple smithy build files? That seems maybe invalid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The AST command takes a config file and it says multiple ones are merged. The intent was to allow this to be at all folder hierarchies.