foobara 0.0.14 → 0.0.16
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 +9 -0
- data/projects/entity/src/concerns/attribute_helpers.rb +190 -0
- data/projects/entity/src/entity.rb +1 -0
- data/projects/entity/src/extensions/builtin_types/entity/casters/hash.rb +1 -0
- data/projects/model/src/extensions/builtin_types/model/transformers/mutable.rb +13 -5
- data/projects/model/src/model.rb +2 -1
- metadata +3 -3
- data/projects/command/src/command/entity_helpers.rb +0 -184
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: a386511b5dd07ad9a067ce02cdf7dfef7436a9db51fe938e26e1dec2c42b836f
         | 
| 4 | 
            +
              data.tar.gz: bf3f4960d49e6ca8f9a17ee7f4ceb7d3f04dd433172bc0028d8314081c933049
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 7cd69b5365baa7bb77534875bed988c638b2c48d79d28119337adca49c8a2551bd958bd3835da682fc143d57186ba7efbf8a36630fc573d0991ae0c10c4cf6bb
         | 
| 7 | 
            +
              data.tar.gz: e00097bd3e762179bd14bfa9e292a2b93c6f063d5dd67b82ec6942d7578af206dd97322a560a7f0e9771bb5b799d2acb34d07e08db4d3f46f6f22ab48f2be642
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,3 +1,12 @@ | |
| 1 | 
            +
            ## [0.0.16] - 2024-11-22
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            - Fix bug where mutable not being set explicitly raises
         | 
| 4 | 
            +
            - Also, make it so models don't default to mutable false when processed by a model type
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            ## [0.0.15] - 2024-11-20
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            - Move entity attributes type declaration helpers from Command::EntityHelpers to Entity for convenience.
         | 
| 9 | 
            +
             | 
| 1 10 | 
             
            ## [0.0.14] - 2024-11-15
         | 
| 2 11 |  | 
| 3 12 | 
             
            - Provide a default execute because why not...
         | 
| @@ -0,0 +1,190 @@ | |
| 1 | 
            +
            module Foobara
         | 
| 2 | 
            +
              class Entity < Model
         | 
| 3 | 
            +
                module Concerns
         | 
| 4 | 
            +
                  module AttributeHelpers
         | 
| 5 | 
            +
                    include Foobara::Concern
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                    def update_aggregate(value, type = self.class.model_type)
         | 
| 8 | 
            +
                      # is this a smell?
         | 
| 9 | 
            +
                      self.class.update_aggregate(self, value, type)
         | 
| 10 | 
            +
                    end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    module ClassMethods
         | 
| 13 | 
            +
                      def attributes_for_update
         | 
| 14 | 
            +
                        attributes_for_aggregate_update
         | 
| 15 | 
            +
                      end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                      # TODO: we should have metadata on the entity about whether it required a primary key
         | 
| 18 | 
            +
                      # upon creation or not instead of an option here.
         | 
| 19 | 
            +
                      def attributes_for_create(includes_primary_key: false)
         | 
| 20 | 
            +
                        return attributes_type if includes_primary_key
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                        declaration = attributes_type.declaration_data
         | 
| 23 | 
            +
                        # TODO: just slice out the element type declarations
         | 
| 24 | 
            +
                        declaration = Util.deep_dup(declaration)
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                        if declaration.key?(:required) && declaration[:required].include?(primary_key_attribute)
         | 
| 27 | 
            +
                          declaration[:required].delete(primary_key_attribute)
         | 
| 28 | 
            +
                        end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                        if declaration.key?(:defaults) && declaration[:defaults].include?(primary_key_attribute)
         | 
| 31 | 
            +
                          declaration[:defaults].delete(primary_key_attribute)
         | 
| 32 | 
            +
                        end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                        if declaration.key?(:element_type_declarations)
         | 
| 35 | 
            +
                          if declaration[:element_type_declarations].key?(primary_key_attribute)
         | 
| 36 | 
            +
                            declaration[:element_type_declarations].delete(primary_key_attribute)
         | 
| 37 | 
            +
                          end
         | 
| 38 | 
            +
                        end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                        handler = Domain.global.foobara_type_builder.handler_for_class(
         | 
| 41 | 
            +
                          TypeDeclarations::Handlers::ExtendAttributesTypeDeclaration
         | 
| 42 | 
            +
                        )
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                        handler.desugarize(declaration)
         | 
| 45 | 
            +
                      end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                      def attributes_for_aggregate_update(initial = true)
         | 
| 48 | 
            +
                        declaration = attributes_type.declaration_data
         | 
| 49 | 
            +
                        # TODO: just slice out the element type declarations
         | 
| 50 | 
            +
                        declaration = Util.deep_dup(declaration)
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                        declaration.delete(:defaults)
         | 
| 53 | 
            +
                        declaration.delete(:required)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                        if initial
         | 
| 56 | 
            +
                          declaration[:required] = [primary_key_attribute]
         | 
| 57 | 
            +
                        end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                        associations.each_pair do |data_path, type|
         | 
| 60 | 
            +
                          if type.extends?(BuiltinTypes[:entity])
         | 
| 61 | 
            +
                            target_class = type.target_class
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                            entry = type_declaration_value_at(declaration, DataPath.new(data_path).path)
         | 
| 64 | 
            +
                            entry.clear
         | 
| 65 | 
            +
                            entry.merge!(target_class.attributes_for_aggregate_update(false))
         | 
| 66 | 
            +
                          end
         | 
| 67 | 
            +
                        end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                        declaration
         | 
| 70 | 
            +
                      end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                      def attributes_for_atom_update
         | 
| 73 | 
            +
                        declaration = attributes_type.declaration_data
         | 
| 74 | 
            +
                        # TODO: just slice out the element type declarations
         | 
| 75 | 
            +
                        declaration = Util.deep_dup(declaration)
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                        declaration.delete(:defaults)
         | 
| 78 | 
            +
                        declaration[:required] = [primary_key_attribute]
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                        # expect all associations to be expressed as primary key values
         | 
| 81 | 
            +
                        # TODO: should we have a special type for encapsulating primary keys types??
         | 
| 82 | 
            +
                        associations.each_pair do |data_path, type|
         | 
| 83 | 
            +
                          if type.extends?(BuiltinTypes[:entity])
         | 
| 84 | 
            +
                            target_class = type.target_class
         | 
| 85 | 
            +
                            # TODO: do we really need declaration_data? Why cant we use the type directly?
         | 
| 86 | 
            +
                            # TODO: make this work with the type directly for performance reasons.
         | 
| 87 | 
            +
                            primary_key_type_declaration = target_class.primary_key_type.declaration_data
         | 
| 88 | 
            +
                            entry = type_declaration_value_at(declaration, DataPath.new(data_path).path)
         | 
| 89 | 
            +
                            entry.clear
         | 
| 90 | 
            +
                            entry.merge!(primary_key_type_declaration)
         | 
| 91 | 
            +
                          end
         | 
| 92 | 
            +
                        end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                        declaration
         | 
| 95 | 
            +
                      end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                      def attributes_for_find_by
         | 
| 98 | 
            +
                        element_type_declarations = {}
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                        attributes_type.element_types.each_pair do |attribute_name, attribute_type|
         | 
| 101 | 
            +
                          element_type_declarations[attribute_name] = attribute_type.reference_or_declaration_data
         | 
| 102 | 
            +
                        end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                        handler = Domain.global.foobara_type_builder.handler_for_class(
         | 
| 105 | 
            +
                          TypeDeclarations::Handlers::ExtendAttributesTypeDeclaration
         | 
| 106 | 
            +
                        )
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                        handler.desugarize(
         | 
| 109 | 
            +
                          type: "::attributes",
         | 
| 110 | 
            +
                          element_type_declarations:
         | 
| 111 | 
            +
                        )
         | 
| 112 | 
            +
                      end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                      def update_aggregate(object, value, type = object.class.model_type)
         | 
| 115 | 
            +
                        return value if object.nil?
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                        if type.extends?(BuiltinTypes[:model])
         | 
| 118 | 
            +
                          element_types = type.element_types.element_types
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                          value.each_pair do |attribute_name, new_value|
         | 
| 121 | 
            +
                            current_value = object.read_attribute(attribute_name)
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                            attribute_type = element_types[attribute_name]
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                            updated_value = update_aggregate(current_value, new_value, attribute_type)
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                            object.write_attribute(attribute_name, updated_value)
         | 
| 128 | 
            +
                          end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                          object
         | 
| 131 | 
            +
                        elsif type.extends?(BuiltinTypes[:attributes])
         | 
| 132 | 
            +
                          element_types = type.element_types
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                          object = object.dup
         | 
| 135 | 
            +
                          object ||= {}
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                          value.each_pair do |attribute_name, new_value|
         | 
| 138 | 
            +
                            current_value = object[attribute_name]
         | 
| 139 | 
            +
                            attribute_type = element_types[attribute_name]
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                            updated_value = update_aggregate(current_value, new_value, attribute_type)
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                            object[attribute_name] = updated_value
         | 
| 144 | 
            +
                          end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                          object
         | 
| 147 | 
            +
                        elsif type.extends?(BuiltinTypes[:tuple])
         | 
| 148 | 
            +
                          # :nocov:
         | 
| 149 | 
            +
                          raise "Tuple not yet supported"
         | 
| 150 | 
            +
                        # :nocov:
         | 
| 151 | 
            +
                        elsif type.extends?(BuiltinTypes[:associative_array])
         | 
| 152 | 
            +
                          # :nocov:
         | 
| 153 | 
            +
                          raise "Associated array not yet supported"
         | 
| 154 | 
            +
                        # :nocov:
         | 
| 155 | 
            +
                        elsif type.extends?(BuiltinTypes[:array])
         | 
| 156 | 
            +
                          element_type = type.element_type
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                          value.map.with_index do |element, index|
         | 
| 159 | 
            +
                            update_aggregate(object[index], element, element_type)
         | 
| 160 | 
            +
                          end
         | 
| 161 | 
            +
                        else
         | 
| 162 | 
            +
                          value
         | 
| 163 | 
            +
                        end
         | 
| 164 | 
            +
                      end
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                      private
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                      def type_declaration_value_at(declaration, path_parts)
         | 
| 169 | 
            +
                        return declaration if path_parts.empty?
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                        path_part, *path_parts = path_parts
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                        declaration = case path_part
         | 
| 174 | 
            +
                                      when :"#"
         | 
| 175 | 
            +
                                        declaration[:element_type_declaration]
         | 
| 176 | 
            +
                                      when Symbol, Integer
         | 
| 177 | 
            +
                                        declaration[:element_type_declarations][path_part]
         | 
| 178 | 
            +
                                      else
         | 
| 179 | 
            +
                                        # :nocov:
         | 
| 180 | 
            +
                                        raise "Bad path part #{path_part}"
         | 
| 181 | 
            +
                                        # :nocov:
         | 
| 182 | 
            +
                                      end
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                        type_declaration_value_at(declaration, path_parts)
         | 
| 185 | 
            +
                      end
         | 
| 186 | 
            +
                    end
         | 
| 187 | 
            +
                  end
         | 
| 188 | 
            +
                end
         | 
| 189 | 
            +
              end
         | 
| 190 | 
            +
            end
         | 
| @@ -11,11 +11,19 @@ module Foobara | |
| 11 11 | 
             
                      end
         | 
| 12 12 |  | 
| 13 13 | 
             
                      def transform(record)
         | 
| 14 | 
            -
                         | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 14 | 
            +
                        if parent_declaration_data.key?(:mutable)
         | 
| 15 | 
            +
                          # hmmmm.... can we really just arbitrarily clobber this?
         | 
| 16 | 
            +
                          # wouldn't that be surprising to calling code that passes in a record/model?
         | 
| 17 | 
            +
                          # One use-case of this seems to be to reduce the amount of possible errors a command reports
         | 
| 18 | 
            +
                          # by declaring that only some subset or none of the attributes are mutable.
         | 
| 19 | 
            +
                          # However, we shouldn't react to this by clobbering the mutable state of the record because it might not
         | 
| 20 | 
            +
                          # be a fresh record fetched from a primary key it might be an already loaded record/model from some other
         | 
| 21 | 
            +
                          # context and that context might be surprised to learn that we've clobbered its mutability status.
         | 
| 22 | 
            +
                          # Solutions?
         | 
| 23 | 
            +
                          # 1. In the case of models, we could duplicate the model if the mutable value is different.
         | 
| 24 | 
            +
                          # 2. But what about entities? We almost need some sort of proxy entity that tightens the mutability?
         | 
| 25 | 
            +
                          record.mutable = parent_declaration_data[:mutable]
         | 
| 26 | 
            +
                        end
         | 
| 19 27 |  | 
| 20 28 | 
             
                        record
         | 
| 21 29 | 
             
                      end
         | 
    
        data/projects/model/src/model.rb
    CHANGED
    
    | @@ -207,6 +207,7 @@ module Foobara | |
| 207 207 | 
             
                    end
         | 
| 208 208 | 
             
                  end
         | 
| 209 209 |  | 
| 210 | 
            +
                  # why do we default to true here but false in the transformers?
         | 
| 210 211 | 
             
                  mutable = options.key?(:mutable) ? options[:mutable] : true
         | 
| 211 212 |  | 
| 212 213 | 
             
                  self.mutable = if mutable.is_a?(::Array)
         | 
| @@ -227,7 +228,7 @@ module Foobara | |
| 227 228 | 
             
                def write_attribute(attribute_name, value)
         | 
| 228 229 | 
             
                  attribute_name = attribute_name.to_sym
         | 
| 229 230 |  | 
| 230 | 
            -
                  if mutable == true || mutable | 
| 231 | 
            +
                  if mutable == true || mutable&.include?(attribute_name)
         | 
| 231 232 | 
             
                    outcome = cast_attribute(attribute_name, value)
         | 
| 232 233 | 
             
                    attributes[attribute_name] = outcome.success? ? outcome.result : value
         | 
| 233 234 | 
             
                  else
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: foobara
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.16
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Miles Georgi
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2024-11- | 
| 11 | 
            +
            date: 2024-11-22 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: foobara-util
         | 
| @@ -116,7 +116,6 @@ files: | |
| 116 116 | 
             
            - projects/callback/src/set.rb
         | 
| 117 117 | 
             
            - projects/command/lib/foobara/command.rb
         | 
| 118 118 | 
             
            - projects/command/src/command.rb
         | 
| 119 | 
            -
            - projects/command/src/command/entity_helpers.rb
         | 
| 120 119 | 
             
            - projects/command/src/concerns/callbacks.rb
         | 
| 121 120 | 
             
            - projects/command/src/concerns/description.rb
         | 
| 122 121 | 
             
            - projects/command/src/concerns/domain_mappers.rb
         | 
| @@ -187,6 +186,7 @@ files: | |
| 187 186 | 
             
            - projects/domain/src/organization_module_extension.rb
         | 
| 188 187 | 
             
            - projects/entity/lib/foobara/entity.rb
         | 
| 189 188 | 
             
            - projects/entity/src/concerns/associations.rb
         | 
| 189 | 
            +
            - projects/entity/src/concerns/attribute_helpers.rb
         | 
| 190 190 | 
             
            - projects/entity/src/concerns/attributes.rb
         | 
| 191 191 | 
             
            - projects/entity/src/concerns/callbacks.rb
         | 
| 192 192 | 
             
            - projects/entity/src/concerns/initialization.rb
         | 
| @@ -1,184 +0,0 @@ | |
| 1 | 
            -
            module Foobara
         | 
| 2 | 
            -
              class Command
         | 
| 3 | 
            -
                module EntityHelpers
         | 
| 4 | 
            -
                  module_function
         | 
| 5 | 
            -
             | 
| 6 | 
            -
                  # Just for convenience
         | 
| 7 | 
            -
                  def attributes_for_update(entity_class)
         | 
| 8 | 
            -
                    type_declaration_for_record_aggregate_update(entity_class)
         | 
| 9 | 
            -
                  end
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                  # TODO: we should have metadata on the entity about whether it required a primary key
         | 
| 12 | 
            -
                  # upon creation or not instead of an option here.
         | 
| 13 | 
            -
                  def attributes_for_create(entity_class, includes_primary_key: false)
         | 
| 14 | 
            -
                    attributes_type = entity_class.attributes_type
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                    return attributes_type if includes_primary_key
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                    declaration = attributes_type.declaration_data
         | 
| 19 | 
            -
                    # TODO: just slice out the element type declarations
         | 
| 20 | 
            -
                    declaration = Util.deep_dup(declaration)
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                    primary_key_attribute = entity_class.primary_key_attribute
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                    if declaration.key?(:required) && declaration[:required].include?(primary_key_attribute)
         | 
| 25 | 
            -
                      declaration[:required].delete(primary_key_attribute)
         | 
| 26 | 
            -
                    end
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                    if declaration.key?(:defaults) && declaration[:defaults].include?(primary_key_attribute)
         | 
| 29 | 
            -
                      declaration[:defaults].delete(primary_key_attribute)
         | 
| 30 | 
            -
                    end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                    if declaration.key?(:element_type_declarations)
         | 
| 33 | 
            -
                      if declaration[:element_type_declarations].key?(primary_key_attribute)
         | 
| 34 | 
            -
                        declaration[:element_type_declarations].delete(primary_key_attribute)
         | 
| 35 | 
            -
                      end
         | 
| 36 | 
            -
                    end
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                    handler = Domain.global.foobara_type_builder.handler_for_class(
         | 
| 39 | 
            -
                      TypeDeclarations::Handlers::ExtendAttributesTypeDeclaration
         | 
| 40 | 
            -
                    )
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                    handler.desugarize(declaration)
         | 
| 43 | 
            -
                  end
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                  def type_declaration_for_record_aggregate_update(entity_class, initial = true)
         | 
| 46 | 
            -
                    declaration = entity_class.attributes_type.declaration_data
         | 
| 47 | 
            -
                    # TODO: just slice out the element type declarations
         | 
| 48 | 
            -
                    declaration = Util.deep_dup(declaration)
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                    declaration.delete(:defaults)
         | 
| 51 | 
            -
                    declaration.delete(:required)
         | 
| 52 | 
            -
             | 
| 53 | 
            -
                    if initial
         | 
| 54 | 
            -
                      declaration[:required] = [entity_class.primary_key_attribute]
         | 
| 55 | 
            -
                    end
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                    entity_class.associations.each_pair do |data_path, type|
         | 
| 58 | 
            -
                      if type.extends?(BuiltinTypes[:entity])
         | 
| 59 | 
            -
                        target_class = type.target_class
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                        entry = type_declaration_value_at(declaration, DataPath.new(data_path).path)
         | 
| 62 | 
            -
                        entry.clear
         | 
| 63 | 
            -
                        entry.merge!(type_declaration_for_record_aggregate_update(target_class, false))
         | 
| 64 | 
            -
                      end
         | 
| 65 | 
            -
                    end
         | 
| 66 | 
            -
             | 
| 67 | 
            -
                    declaration
         | 
| 68 | 
            -
                  end
         | 
| 69 | 
            -
             | 
| 70 | 
            -
                  def type_declaration_for_record_atom_update(entity_class)
         | 
| 71 | 
            -
                    declaration = entity_class.attributes_type.declaration_data
         | 
| 72 | 
            -
                    # TODO: just slice out the element type declarations
         | 
| 73 | 
            -
                    declaration = Util.deep_dup(declaration)
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                    declaration.delete(:defaults)
         | 
| 76 | 
            -
                    declaration[:required] = [entity_class.primary_key_attribute]
         | 
| 77 | 
            -
             | 
| 78 | 
            -
                    # expect all associations to be expressed as primary key values
         | 
| 79 | 
            -
                    # TODO: should we have a special type for encapsulating primary keys types??
         | 
| 80 | 
            -
                    entity_class.associations.each_pair do |data_path, type|
         | 
| 81 | 
            -
                      if type.extends?(BuiltinTypes[:entity])
         | 
| 82 | 
            -
                        target_class = type.target_class
         | 
| 83 | 
            -
                        # TODO: do we really need declaration_data? Why cant we use the type directly?
         | 
| 84 | 
            -
                        # TODO: make this work with the type directly for performance reasons.
         | 
| 85 | 
            -
                        primary_key_type_declaration = target_class.primary_key_type.declaration_data
         | 
| 86 | 
            -
                        entry = type_declaration_value_at(declaration, DataPath.new(data_path).path)
         | 
| 87 | 
            -
                        entry.clear
         | 
| 88 | 
            -
                        entry.merge!(primary_key_type_declaration)
         | 
| 89 | 
            -
                      end
         | 
| 90 | 
            -
                    end
         | 
| 91 | 
            -
             | 
| 92 | 
            -
                    declaration
         | 
| 93 | 
            -
                  end
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                  def type_declaration_for_find_by(entity_class)
         | 
| 96 | 
            -
                    element_type_declarations = {}
         | 
| 97 | 
            -
             | 
| 98 | 
            -
                    entity_class.attributes_type.element_types.each_pair do |attribute_name, attribute_type|
         | 
| 99 | 
            -
                      element_type_declarations[attribute_name] = attribute_type.reference_or_declaration_data
         | 
| 100 | 
            -
                    end
         | 
| 101 | 
            -
             | 
| 102 | 
            -
                    handler = Domain.global.foobara_type_builder.handler_for_class(
         | 
| 103 | 
            -
                      TypeDeclarations::Handlers::ExtendAttributesTypeDeclaration
         | 
| 104 | 
            -
                    )
         | 
| 105 | 
            -
             | 
| 106 | 
            -
                    handler.desugarize(
         | 
| 107 | 
            -
                      type: "::attributes",
         | 
| 108 | 
            -
                      element_type_declarations:
         | 
| 109 | 
            -
                    )
         | 
| 110 | 
            -
                  end
         | 
| 111 | 
            -
             | 
| 112 | 
            -
                  def update_aggregate(object, value, type = object.class.model_type)
         | 
| 113 | 
            -
                    return value if object.nil?
         | 
| 114 | 
            -
             | 
| 115 | 
            -
                    if type.extends?(BuiltinTypes[:model])
         | 
| 116 | 
            -
                      element_types = type.element_types.element_types
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                      value.each_pair do |attribute_name, new_value|
         | 
| 119 | 
            -
                        current_value = object.read_attribute(attribute_name)
         | 
| 120 | 
            -
             | 
| 121 | 
            -
                        attribute_type = element_types[attribute_name]
         | 
| 122 | 
            -
             | 
| 123 | 
            -
                        updated_value = update_aggregate(current_value, new_value, attribute_type)
         | 
| 124 | 
            -
             | 
| 125 | 
            -
                        object.write_attribute(attribute_name, updated_value)
         | 
| 126 | 
            -
                      end
         | 
| 127 | 
            -
             | 
| 128 | 
            -
                      object
         | 
| 129 | 
            -
                    elsif type.extends?(BuiltinTypes[:attributes])
         | 
| 130 | 
            -
                      element_types = type.element_types
         | 
| 131 | 
            -
             | 
| 132 | 
            -
                      object = object.dup
         | 
| 133 | 
            -
                      object ||= {}
         | 
| 134 | 
            -
             | 
| 135 | 
            -
                      value.each_pair do |attribute_name, new_value|
         | 
| 136 | 
            -
                        current_value = object[attribute_name]
         | 
| 137 | 
            -
                        attribute_type = element_types[attribute_name]
         | 
| 138 | 
            -
             | 
| 139 | 
            -
                        updated_value = update_aggregate(current_value, new_value, attribute_type)
         | 
| 140 | 
            -
             | 
| 141 | 
            -
                        object[attribute_name] = updated_value
         | 
| 142 | 
            -
                      end
         | 
| 143 | 
            -
             | 
| 144 | 
            -
                      object
         | 
| 145 | 
            -
                    elsif type.extends?(BuiltinTypes[:tuple])
         | 
| 146 | 
            -
                      # :nocov:
         | 
| 147 | 
            -
                      raise "Tuple not yet supported"
         | 
| 148 | 
            -
                    # :nocov:
         | 
| 149 | 
            -
                    elsif type.extends?(BuiltinTypes[:associative_array])
         | 
| 150 | 
            -
                      # :nocov:
         | 
| 151 | 
            -
                      raise "Associated array not yet supported"
         | 
| 152 | 
            -
                    # :nocov:
         | 
| 153 | 
            -
                    elsif type.extends?(BuiltinTypes[:array])
         | 
| 154 | 
            -
                      element_type = type.element_type
         | 
| 155 | 
            -
             | 
| 156 | 
            -
                      value.map.with_index do |element, index|
         | 
| 157 | 
            -
                        update_aggregate(object[index], element, element_type)
         | 
| 158 | 
            -
                      end
         | 
| 159 | 
            -
                    else
         | 
| 160 | 
            -
                      value
         | 
| 161 | 
            -
                    end
         | 
| 162 | 
            -
                  end
         | 
| 163 | 
            -
             | 
| 164 | 
            -
                  def type_declaration_value_at(declaration, path_parts)
         | 
| 165 | 
            -
                    return declaration if path_parts.empty?
         | 
| 166 | 
            -
             | 
| 167 | 
            -
                    path_part, *path_parts = path_parts
         | 
| 168 | 
            -
             | 
| 169 | 
            -
                    declaration = case path_part
         | 
| 170 | 
            -
                                  when :"#"
         | 
| 171 | 
            -
                                    declaration[:element_type_declaration]
         | 
| 172 | 
            -
                                  when Symbol, Integer
         | 
| 173 | 
            -
                                    declaration[:element_type_declarations][path_part]
         | 
| 174 | 
            -
                                  else
         | 
| 175 | 
            -
                                    # :nocov:
         | 
| 176 | 
            -
                                    raise "Bad path part #{path_part}"
         | 
| 177 | 
            -
                                    # :nocov:
         | 
| 178 | 
            -
                                  end
         | 
| 179 | 
            -
             | 
| 180 | 
            -
                    type_declaration_value_at(declaration, path_parts)
         | 
| 181 | 
            -
                  end
         | 
| 182 | 
            -
                end
         | 
| 183 | 
            -
              end
         | 
| 184 | 
            -
            end
         |