# frozen_string_literal: truerequire_relative"typed_record"moduleAttio# Represents a person record in Attio# Provides convenient methods for working with people and their attributesclassPerson<TypedRecordobject_type"people"# Set the person's name using a more intuitive interface# @param first [String] First name# @param last [String] Last name# @param middle [String] Middle name (optional)# @param full [String] Full name (optional, will be generated if not provided)defset_name(first: nil,last: nil,middle: nil,full: nil)name_data={}name_data[:first_name]=firstiffirstname_data[:last_name]=lastiflastname_data[:middle_name]=middleifmiddle# Generate full name if not providediffullname_data[:full_name]=fullelsiffirst||lastparts=[first,middle,last].compactname_data[:full_name]=parts.join(" ")unlessparts.empty?end# Attio expects name as an array with a single hashself[:name]=[name_data]unlessname_data.empty?end# Set the person's name using a hash or string# @param name_value [Hash, String] Either a hash with first/last/middle/full keys or a full name stringdefname=(name_value)casename_valuewhenHashset_name(**name_value)whenStringset_name(full: name_value)elseraiseArgumentError,"Name must be a Hash or String"endend# Get the person's full name# @return [String, nil] The full name or nil if not setdeffull_nameextract_name_field("full_name")end# Get the person's first name# @return [String, nil] The first name or nil if not setdeffirst_nameextract_name_field("first_name")end# Get the person's last name# @return [String, nil] The last name or nil if not setdeflast_nameextract_name_field("last_name")endprivate# Extract a field from the name data structure# @param field [String] The field to extract# @return [String, nil] The field value or nildefextract_name_field(field)name_value=self[:name]returnnilunlessname_valuename_hash=normalize_to_hash(name_value)name_hash[field]||name_hash[field.to_sym]end# Extract primary value from various data structures# @param value [Array, Hash, Object] The value to extract from# @param field [String] The field name for hash extraction# @return [String, nil] The extracted valuedefextract_primary_value(value,field)casevaluewhenArrayreturnnilifvalue.empty?first_item=value.firstiffirst_item.is_a?(Hash)first_item[field]||first_item[field.to_sym]elsefirst_item.to_sendwhenHashvalue[field]||value[field.to_sym]elsevalue.to_sendend# Normalize various name formats to a hash# @param value [Array, Hash] The value to normalize# @return [Hash] The normalized hashdefnormalize_to_hash(value)casevaluewhenArrayvalue.first.is_a?(Hash)?value.first:{}whenHashvalueelse{}endendpublic# Add an email address# @param email [String] The email address to adddefadd_email(email)emails=self[:email_addresses]||[]# Ensure it's an arrayemails=[emails]unlessemails.is_a?(Array)# Add the email if it's not already presentemails<<emailunlessemails.include?(email)self[:email_addresses]=emailsend# Get the primary email address# @return [String, nil] The primary email or nil if not setdefemailemails=self[:email_addresses]returnnilunlessemailsextract_primary_value(emails,"email_address")end# Add a phone number# @param number [String] The phone number# @param country_code [String] The country code (e.g., "US")defadd_phone(number,country_code: "US")phones=self[:phone_numbers]||[]phones=[phones]unlessphones.is_a?(Array)phone_data={original_phone_number: number,country_code: country_code}phones<<phone_dataself[:phone_numbers]=phonesend# Get the primary phone number# @return [String, nil] The primary phone number or nil if not setdefphonephones=self[:phone_numbers]returnnilunlessphonesextract_primary_value(phones,"original_phone_number")end# Set the job title# @param title [String] The job titledefjob_title=(title)self[:job_title]=titleend# Associate with a company# @param company [Company, String] A Company instance or company IDdefcompany=(company)ifcompany.is_a?(Company)# Extract ID properly from company instancecompany_id=company.id.is_a?(Hash)?company.id["record_id"]:company.idself[:company]=[{target_object: "companies",target_record_id: company_id}]elsifcompany.is_a?(String)self[:company]=[{target_object: "companies",target_record_id: company}]elsifcompany.nil?self[:company]=nilelseraiseArgumentError,"Company must be a Company instance or ID string"endendclass<<self# Create a person with a simplified interface# @param attributes [Hash] Person attributes# @option attributes [String] :first_name First name# @option attributes [String] :last_name Last name# @option attributes [String] :email Email address# @option attributes [String] :phone Phone number# @option attributes [String] :job_title Job title# @option attributes [Hash] :values Raw values hash (for advanced use)defcreate(first_name: nil,last_name: nil,full_name: nil,email: nil,phone: nil,job_title: nil,company: nil,values: {},**opts)# Build the values hashvalues[:name]||=[]iffirst_name||last_name||full_namename_data={}# If only full_name is provided, try to parse itiffull_name&&!first_name&&!last_nameparts=full_name.split(" ")ifparts.length>=2name_data[:first_name]=parts.firstname_data[:last_name]=parts[1..].join(" ")elsename_data[:first_name]=full_nameendname_data[:full_name]=full_nameelsename_data[:first_name]=first_nameiffirst_namename_data[:last_name]=last_nameiflast_namename_data[:full_name]=full_name||[first_name,last_name].compact.join(" ")endvalues[:name]=[name_data]endvalues[:email_addresses]=[email]ifemail&&!values[:email_addresses]ifphone&&!values[:phone_numbers]values[:phone_numbers]=[{original_phone_number: phone,country_code: opts.delete(:country_code)||"US"}]endvalues[:job_title]=job_titleifjob_title&&!values[:job_title]ifcompany&&!values[:company]company_ref=ifcompany.is_a?(Company)company_id=company.id.is_a?(Hash)?company.id["record_id"]:company.id{target_object: "companies",target_record_id: company_id}elsifcompany.is_a?(String){target_object: "companies",target_record_id: company}endvalues[:company]=[company_ref]ifcompany_refendsuper(values: values,**opts)end# Search people by query# @param query [String] Query to search fordefsearch(query,**opts)# Search across name fieldslist(**opts.merge(filter: {"$or":[{name: {first_name: {"$contains":query}}},{name: {last_name: {"$contains":query}}},{name: {full_name: {"$contains":query}}}]}))endprivate# Build filter for email fielddeffilter_by_email(value){email_addresses: {email_address: {"$eq":value}}}end# Build filter for name field (searches across first, last, and full name)deffilter_by_name(value){"$or":[{name: {first_name: {"$contains":value}}},{name: {last_name: {"$contains":value}}},{name: {full_name: {"$contains":value}}}]}endendend# Convenience aliasPeople=Personend