lib/hubspot/resource.rb in ruby_hubspot_api-0.2.2 vs lib/hubspot/resource.rb in ruby_hubspot_api-0.3.0

- old
+ new

@@ -43,16 +43,18 @@ class << self # Find a resource by ID and return an instance of the class # # id - [Integer] The ID (or hs_object_id) of the resource to fetch. + # properties - an array of property names to fetch in the result # # Example: # contact = Hubspot::Contact.find(1) + # contact = Hubspot::Contact.find(1, properties: %w[email firstname lastname custom_field]) # # Returns An instance of the resource. - def find(id, properties = nil) + def find(id, properties: nil) all_properties = build_property_list(properties) if all_properties.is_a?(Array) && !all_properties.empty? params = { query: { properties: all_properties } } end response = get("#{api_root}/#{resource_name}/#{id}", params || {}) @@ -179,12 +181,12 @@ # # Example: # Hubspot::Contact.batch_read_all(hubspot_contact_ids) # # Returns [Hubspot::Batch] A batch of resources that can be operated on further - def batch_read_all(object_ids = [], id_property: 'id') - Hubspot::Batch.read(self, object_ids, id_property: id_property) + def batch_read_all(object_ids = [], properties: [], id_property: 'id') + Hubspot::Batch.read(self, object_ids, properties: properties, id_property: id_property) end # Retrieve the complete list of properties for this resource class # # Returns [Array<Hubspot::Property>] An array of hubspot properties @@ -390,12 +392,17 @@ # rubocop:disable Lint/MissingSuper # Public: Initialize a resouce # - # data - [2D Hash, nested Hash] data to initialise the resourse This can be either: - # - A Simple 2D Hash, key value pairs of property => value (for the create option) + # data - [2D Hash, nested Hash] data to initialise: + # - The response from the api will be of the form: + # { id: <hs_object_id>, properties: { "email": "john@example.org" ... }, ... } + # + # - A Simple 2D Hash, key value pairs in the form: + # { email: 'john@example.org', firstname: 'John', lastname: 'Smith' } + # # - A structured hash consisting of { id: <hs_object_id>, properties: {}, ... } # This is the same structure as per the API, and can be rebuilt if you store the id # of the object against your own data # # Example: @@ -407,11 +414,11 @@ # puts "Contact saved with hubspot id #{contact.id}" # # existing_contact = Hubspot::Contact.new(id: hubspot_id, properties: contact.to_hubspot) def initialize(data = {}) data.transform_keys!(&:to_s) - @id = extract_id(data) + @id = extract_id(data.delete('id')) @properties = {} @metadata = {} if @id initialize_from_api(data) else @@ -518,19 +525,12 @@ # Handle setters if method_name.end_with?('=') attribute = method_name.chomp('=') new_value = args.first - - # Track changes only if the value has actually changed - if @properties[attribute] != new_value - @changes[attribute] = new_value - else - @changes.delete(attribute) # Remove from changes if it reverts to the original value - end - - return new_value + add_accessors attribute + return send("#{attribute}=", new_value) # Handle getters else return @changes[method_name] if @changes.key?(method_name) return @properties[method_name] if @properties.key?(method_name) end @@ -545,48 +545,78 @@ def respond_to_missing?(method_name, include_private = false) property_name = method_name.to_s.chomp('=') @properties.key?(property_name) || @changes.key?(property_name) || super end - private - - # Extract ID from data and convert to integer - def extract_id(data) - data['id'] ? data['id'].to_i : nil - end - # Initialize from API response, separating metadata from properties def initialize_from_api(data) + @changes = data.delete('changes')&.transform_keys!(&:to_s) || {} + if data['properties'] @metadata = data.reject { |key, _v| key == 'properties' } handle_properties(data['properties']) else handle_properties(data) end + end - @changes = {} + private + + # Extract ID from data and convert to integer + def extract_id(id) + id&.to_i end def handle_properties(properties_data) - properties_data.each do |key, value| - if METADATA_FIELDS.include?(key) - @metadata[key] = value + properties_data.each do |attribute, value| + if metadata_field?(attribute) + @metadata[attribute.to_s] = value else - @properties[key] = value + add_accessors attribute.to_s + @properties[attribute.to_s] = value end end end + def add_accessors(attribute) + add_accessors_setter(attribute) + add_accessors_getter(attribute) + end + + def add_accessors_setter(attribute) + # Define the setter method + define_singleton_method("#{attribute}=") do |new_value| + # Track changes only if the value has actually changed + if @properties[attribute] != new_value + @changes[attribute] = new_value + else + @changes.delete(attribute) # Remove from changes if it reverts to the original value + end + + new_value + end + end + + def add_accessors_getter(attribute) + # Define the getter method + define_singleton_method(attribute) do + # Return from changes if available, else return from properties + return @changes[attribute] if @changes.key?(attribute) + + @properties[attribute] if @properties.key?(attribute) + end + end + + # allows overwriting in other resource classes + def metadata_field?(key) + METADATA_FIELDS.include?(key) + end + # Initialize a new object (no API response) def initialize_new_object(data) @properties = {} @changes = data.transform_keys(&:to_s) @metadata = {} - end - - # Extract metadata from data, excluding properties - def extract_metadata(data) - data.reject { |key, _| key == 'properties' } end # Create a new resource def create_new created_resource = self.class.create(@changes)