Module: ActiveRpc::ModelExtensions

Extended by:
ActiveSupport::Concern
Defined in:
lib/active_rpc/model_extensions.rb,
lib/active_rpc/model_extensions/attribute_dsl.rb

Defined Under Namespace

Classes: AttributeDsl

Class Method Summary collapse

Class Method Details

._active_rpc_extract_field_paths(params, prefix = '') ⇒ Object

Module-level method to extract all field paths from a nested hash structure Examples:

{id: "22", user_roles_list: []} -> ["id", "user_roles_list"]
{id: "22", avatar_attributes: {filename: "test.jpg"}} -> ["id", "avatar_attributes", "avatar_attributes.filename"]
{user_roles_list: [{id: "1", role_id: "admin"}]} -> ["user_roles_list", "user_roles_list[0]", "user_roles_list[0].id", "user_roles_list[0].role_id"]


1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
# File 'lib/active_rpc/model_extensions.rb', line 1396

def self._active_rpc_extract_field_paths(params, prefix = '')
  return [] unless params.respond_to?(:each)

  field_paths = []

  params.each do |key, value|
    # Skip internal fields (starting with _)
    next if key.to_s.start_with?('_')

    current_path = prefix.empty? ? key.to_s : "#{prefix}.#{key}"

    # Always include the current field path
    field_paths << current_path

    if value.is_a?(Hash) && value.any?
      # Preserve original dot notation for nested hashes
      field_paths.concat(_active_rpc_extract_field_paths(value, current_path))

      # Also emit bracket notation so protobuf map fields keep key-level metadata
      value.each do |map_key, map_value|
        map_path = "#{current_path}[#{map_key}]"
        field_paths << map_path
        field_paths.concat(_active_rpc_extract_field_paths(map_value, map_path))
      end
      next
    end

    # Handle arrays with hash elements (like user_roles_list)
    next unless value.is_a?(Array)

    # Include the array field itself even if empty
    value.each_with_index do |item, index|
      next unless item.is_a?(Hash)

      item_path = "#{current_path}[#{index}]"
      field_paths << item_path
      field_paths.concat(_active_rpc_extract_field_paths(item, item_path))
    end
  end

  field_paths.uniq
end

.format_foreign_key_value(model_class, foreign_key, value) ⇒ Object

Helper method to format foreign key values based on database column type This ensures compatibility between ActiveRecord column types and protobuf field types



1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
# File 'lib/active_rpc/model_extensions.rb', line 1441

def self.format_foreign_key_value(model_class, foreign_key, value)
  return nil if value.blank?

  # Get the column type from the model's database schema
  column = model_class.columns_hash[foreign_key.to_s]

  if %i[integer bigint].include?(column&.type)
    # Send as integer for integer/bigint columns to match protobuf int64 fields
    value.to_i
  else
    # Send as string for all other types (string, uuid, etc.)
    value.to_s
  end
end

.prepare_value_for_rpc(value, type = nil) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/active_rpc/model_extensions.rb', line 13

def self.prepare_value_for_rpc(value, type = nil)
  prepared =
    if type&.respond_to?(:rpc_serialize)
      type.rpc_serialize(value)
    elsif value.respond_to?(:rpc_serialize)
      value.rpc_serialize
    elsif value.respond_to?(:as_rpc_payload)
      value.as_rpc_payload
    elsif value.respond_to?(:as_json)
      value.as_json
    else
      value
    end

  if defined?(ActiveSupport::HashWithIndifferentAccess) && prepared.is_a?(ActiveSupport::HashWithIndifferentAccess)
    prepared = prepared.to_h
  end

  case prepared
  when Hash
    prepared.deep_stringify_keys
  when Array
    prepared.map { |item| prepare_value_for_rpc(item, nil) }
  else
    prepared
  end
end