use Red::Model; use Red::AST::Value; use Red::AST::Infixes; use Red::Attr::Column; =head2 MetamodelX::Red::Id unit role MetamodelX::Red::Id; has $!id-values-attr; method columns { ... } #method set-attr { ... } #method set-dirty { ... } method id-values-attr(|) { $!id-values-attr; } sub id-values-attr-build(\type, | --> Hash){ {} } method set-helper-attrs(Mu \type) { my %attr = type.^attributes.map( -> $a { $a.name => $a }).Hash; if %attr<%!___ID_VALUES___> -> $a { $!id-values-attr //= $a; } else { $!id-values-attr = Attribute.new: :name<%!___ID_VALUES___>, :package(type), :type(Hash), :!has_accessor; $!id-values-attr.set_build: &id-values-attr-build; type.^add_attribute: $!id-values-attr; } } #| Checks if the given attribute is a primary key of the model. multi method is-id($, Red::Attr::Column $attr) { so $attr.column.id } multi method is-id($, $ --> False) {} #| Gets a list of ids method id(Mu \type) { self.columns(type).grep(*.column.id).list } #| Returns a list of attributes that are either primary keys or marked as unique. method general-ids(\model) { (|model.^id, |model.^unique-constraints) } #| Sets ids method populate-ids(Red::Model:D $model) { $model.^reset-id; $model.^set-id: $model.^id.map({ .name => .get_value: $model }).Hash; } #| resets id multi method reset-id(Red::Model:D $model) { $!id-values-attr.set_value: $model, {} } #| Sets ids multi method set-id(Red::Model:D $model, %ids --> Hash()) { my %attrs = |$model.^id.map({ .name => $_ }); for %ids.kv -> Str $name, $val { $!id-values-attr.get_value($model).{ $name } = $model.^get-attr: $name without $!id-values-attr.get_value($model).{ $name }; self.set-attr: $model, $name, $val; $model.^set-dirty: %attrs{$name}; } } #| Sets id multi method set-id(Red::Model:D $model, $id where !.^isa: Associative --> Hash()) { my $col = $model.^id.head; $!id-values-attr.get_value($model).{ $col.name } = $model.^get-attr: $col without $!id-values-attr.get_value($model).{ $col.name }; self.set-attr: $model, $col, $id; $model.^set-dirty: $col; } #| Returns a Hash with an id map multi method id-map(Red::Model $model, $id --> Hash()) { die "$model.^name() has no id" unless $model.^id; $model.^id.head.name.substr(2) => $id } multi method id-hash(Red::Model $model --> Hash()) { $model.^id.map({ my $name = .name.substr(2); $name => $model."$name"() }) } #| Returns a filter using the id multi method id-filter(Red::Model:D $model) { my @a = $model.^general-ids.flat.map({ Red::AST::Eq.new: :bind-right, .column, ast-value :type(.type), $!id-values-attr.get_value($model).{ .name } // self.get-old-attr($model, $_) // self.get-attr: $model, $_ }); @a.elems >= 2 ?? @a.reduce: { Red::AST::AND.new: $^a, $^b } !! @a[0] } #| Returns a filter using the id multi method id-filter(Red::Model:U $model, $id) { die "Model must have only 1 id to use id-filter this way" if $model.^id.elems != 1; self.id-filter: $model, |{$model.^id.head.column.attr-name => $id} } # TODO: fix, $model.^general-ids can return an list of lists (for counstraints of more that one column) #| Returns a filter using the id multi method id-filter(Red::Model:U $model, *%data) { # where { .keys.all ~~ $model.^general-ids.flat.map(*.column.attr-name).any }) { my %cols := set $model.^general-ids.flat.map(*.column.attr-name); my @not-ids = %data.keys.grep: { not %cols{ $_ } }; die "one of the following keys aren't ids: { @not-ids.join: ", " }" if @not-ids; $model.^general-ids.flat .map({ next without %data{.column.attr-name}; Red::AST::Eq.new: .column, ast-value %data{.column.attr-name} }) .reduce: { Red::AST::AND.new: $^a, $^b } ; } #multi method id-filter(Red::Model:U $model, *%data) { # die "one of the following keys aren't ids: { %data.keys.join: ", " }" #}