lib/hubspot/resource.rb in ruby_hubspot_api-0.2.1 vs lib/hubspot/resource.rb in ruby_hubspot_api-0.2.1.1
- old
+ new
@@ -4,147 +4,59 @@
require_relative './paged_collection'
require_relative './paged_batch'
module Hubspot
# rubocop:disable Metrics/ClassLength
-
- # HubSpot Resource Base Class
- # This class provides common functionality for interacting with HubSpot API resources such as Contacts, Companies, etc
- #
- # It supports common operations like finding, creating, updating, and deleting resources, as well as batch operations.
- #
- # This class is meant to be inherited by specific resources like `Hubspot::Contact`.
- #
- # You can access the properties of a resource instance by calling the property name as method
- #
- # Example Usage:
- # Hubspot::Contact.find(1)
- # contact.name # 'Luke'
- #
- # company = Hubspot::Company.create(name: "Acme Corp")
- # company.id.nil? # false
- #
+ # Hubspot::Resource class
class Resource < ApiClient
METADATA_FIELDS = %w[createdate hs_object_id lastmodifieddate].freeze
- # Allow read/write access to id, properties, changes and metadata
+ # Allow read/write access to properties and metadata
+ attr_accessor :id, :properties, :changes, :metadata
- # the id of the object in hubspot
- attr_accessor :id
-
- # the properties as if read from the api
- attr_accessor :properties
-
- # track any changes made to properties before saving etc
- attr_accessor :changes
-
- # any other data sent from the api about the resource
- attr_accessor :metadata
-
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.
- #
- # Example:
- # contact = Hubspot::Contact.find(1)
- #
- # Returns An instance of the resource.
def find(id)
response = get("/crm/v3/objects/#{resource_name}/#{id}")
instantiate_from_response(response)
end
- # Finds a resource by a given property and value.
- #
- # property - The property to search by (e.g., "email").
- # value - The value of the property to match.
- # properties - Optional list of properties to return.
- #
- # Example:
- # properties = %w[firstname lastname email last_contacted]
- # contact = Hubspot::Contact.find_by("email", "john@example.com", properties)
- #
- # Returns An instance of the resource.
def find_by(property, value, properties = nil)
params = { idProperty: property }
params[:properties] = properties if properties.is_a?(Array)
response = get("/crm/v3/objects/#{resource_name}/#{value}", query: params)
instantiate_from_response(response)
end
- # Creates a new resource with the given parameters.
- #
- # params - The properties to create the resource with.
- #
- # Example:
- # contact = Hubspot::Contact.create(name: "John Doe", email: "john@example.com")
- #
- # Returns [Resource] The newly created resource.
+ # Create a new resource
def create(params)
response = post("/crm/v3/objects/#{resource_name}", body: { properties: params }.to_json)
instantiate_from_response(response)
end
- # Updates an existing resource by ID.
- #
- # id - The ID of the resource to update.
- # params - The properties to update.
- #
- # Example:
- # contact.update(1, name: "Jane Doe")
- #
- # Returns True if the update was successful, false if not
def update(id, params)
response = patch("/crm/v3/objects/#{resource_name}/#{id}", body: { properties: params }.to_json)
raise Hubspot.error_from_response(response) unless response.success?
true
end
- # Deletes a resource by ID.
- #
- # id - The ID of the resource to delete.
- #
- # Example:
- # Hubspot::Contact.archive(1)
- #
- # Returns True if the deletion was successful, false if not
def archive(id)
response = delete("/crm/v3/objects/#{resource_name}/#{id}")
raise Hubspot.error_from_response(response) unless response.success?
true
end
- # Lists all resources with optional filters and pagination.
- #
- # params - Optional parameters to filter or paginate the results.
- #
- # Example:
- # contacts = Hubspot::Contact.list(limit: 100)
- #
- # Returns [PagedCollection] A collection of resources.
def list(params = {})
PagedCollection.new(
url: "/crm/v3/objects/#{resource_name}",
params: params,
resource_class: self
)
end
- # Performs a batch read operation to retrieve multiple resources by their IDs.
- #
- # object_ids - A list of resource IDs to fetch.
- #
- # id_property - The property to use for identifying resources (default: 'id').
- #
- #
- # Example:
- # Hubspot::Contact.batch_read([1, 2, 3])
- #
- # Returns [PagedBatch] A paged batch of resources (call .each_page to cycle through pages from the API)
def batch_read(object_ids = [], id_property: 'id')
params = id_property == 'id' ? {} : { idProperty: id_property }
PagedBatch.new(
url: "/crm/v3/objects/#{resource_name}/batch/read",
@@ -152,62 +64,34 @@
object_ids: object_ids,
resource_class: self
)
end
- # Performs a batch read operation to retrieve multiple resources by their IDs
- # until there are none left
- #
- # object_ids - A list of resource IDs to fetch. [Array<Integer>]
- # id_property - The property to use for identifying resources (default: 'id').
- #
- # Example:
- # Hubspot::Contact.batch_read([1, 2, 3])
- #
- # 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)
end
- # Retrieve the complete list of properties for this resource class
- #
- # Returns [Array<Hubspot::Property>] An array of hubspot properties
+ # Get the complete list of fields (properties) for the object
def properties
@properties ||= begin
response = get("/crm/v3/properties/#{resource_name}")
handle_response(response)['results'].map { |hash| Property.new(hash) }
end
end
- # Retrieve the complete list of user defined properties for this resource class
- #
- # Returns [Array<Hubspot::Property>] An array of hubspot properties
def custom_properties
properties.reject { |property| property['hubspotDefined'] }
end
- # Retrieve the complete list of updatable properties for this resource class
- #
- # Returns [Array<Hubspot::Property>] An array of updateable hubspot properties
def updatable_properties
properties.reject(&:read_only?)
end
- # Retrieve the complete list of read-only properties for this resource class
- #
- # Returns [Array<Hubspot::Property>] An array of read-only hubspot properties
def read_only_properties
- properties.select(&:read_only)
+ properties.select(&:read_only?)
end
- # Retrieve information about a specific property
- #
- # Example:
- # property = Hubspot::Contact.property('industry_sector')
- # values_for_select = property.options.each_with_object({}) { |prop, ps| ps[prop['value']] = prop['label'] }
- #
- # Returns [Array<Hubspot::Property>] An array of hubspot properties
def property(property_name)
properties.detect { |prop| prop.name == property_name }
end
# Simplified search interface
@@ -220,49 +104,10 @@
'_neq' => 'NEQ',
'_in' => 'IN'
}.freeze
# rubocop:disable Metrics/MethodLength
-
- # Search for resources using a flexible query format and optional properties.
- #
- # This method allows searching for resources by passing a query in the form of a string (for full-text search)
- # or a hash with special suffixes on the keys to define different comparison operators.
- # You can also specify which properties to return and the number of results per page.
- #
- # Available suffixes for query keys (when using a hash):
- # - `_contains`: Matches values that contain the given string.
- # - `_gt`: Greater than comparison.
- # - `_lt`: Less than comparison.
- # - `_gte`: Greater than or equal to comparison.
- # - `_lte`: Less than or equal to comparison.
- # - `_neq`: Not equal to comparison.
- # - `_in`: Matches any of the values in the given array.
- #
- # If no suffix is provided, the default comparison is equality (`EQ`).
- #
- # query - [String, Hash] The query for searching. This can be either:
- # - A String: for full-text search.
- # - A Hash: where each key represents a property and may have suffixes for the comparison
- # (e.g., `{ email_contains: 'example.org', age_gt: 30 }`).
- # properties - An optional array of property names to return in the search results. [Array<String>]
- # If not specified or empty, HubSpot will return the default set of properties.
- # page_size - The number of results to return per page (default is 10 for contacts and 100 for everything else).
- #
- # Example Usage:
- # # Full-text search for 'example.org':
- # contacts = Hubspot::Contact.search(query: "example.org",
- # properties: ["email", "firstname", "lastname"], page_size: 50)
- #
- # # Search for contacts whose email contains 'example.org' and are older than 30:
- # contacts = Hubspot::Contact.search(
- # query: { email_contains: 'example.org', age_gt: 30 },
- # properties: ["email", "firstname", "lastname"],
- # page_size: 50
- # )
- #
- # Returns [PagedCollection] A paged collection of results that can be iterated over.
def search(query:, properties: [], page_size: 100)
search_body = {}
# Add properties if specified
search_body[:properties] = properties unless properties.empty?
@@ -338,27 +183,10 @@
{ propertyName: key.to_s, operator: 'EQ' }
end
end
# rubocop:disable Ling/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)
- # - 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:
- # contact = Hubspot::Contact.new(firstname: 'Luke', lastname: 'Skywalker', email: 'luke@jedi.org')
- # contact.persisted? # false
- # contact.save # creates the record in Hubspot
- # contact.persisted? # true
- # puts "Contact saved with hubspot id #{contact.id}"
- #
- # existing_contact = Hubspot::Contact.new(id: hubspot_id, properties: contact.to_hubspot_properties)
def initialize(data = {})
data.transform_keys!(&:to_s)
@id = extract_id(data)
@properties = {}
@metadata = {}
@@ -366,27 +194,17 @@
initialize_from_api(data)
else
initialize_new_object(data)
end
end
-
# rubocop:enable Ling/MissingSuper
- # Determine the state of the object
- #
- # Returns Boolean
def changes?
!@changes.empty?
end
- # Create or Update the resource.
- # If the resource was already persisted (e.g. it was retrieved from the API)
- # it will be updated using values from @changes
- #
- # If the resource is new (no id) it will be created
- #
- # Returns Boolean
+ # Instance methods for update (or save)
def save
if persisted?
self.class.update(@id, @changes).tap do |result|
return false unless result
@@ -396,57 +214,36 @@
else
create_new
end
end
- # If the resource exists in Hubspot
- #
- # Returns Boolean
def persisted?
@id ? true : false
end
# Update the resource
- #
- # params - hash of properties to update in key value pairs
- #
- # Example:
- # contact = Hubspot::Contact.find(hubspot_contact_id)
- # contact.update(status: 'gold customer', last_contacted_at: Time.now.utc.iso8601)
- #
- # Returns Boolean
def update(params)
raise 'Not able to update as not persisted' unless persisted?
params.each do |key, value|
send("#{key}=", value) # This will trigger the @changes tracking via method_missing
end
save
end
- # Archive the object in Hubspot
- #
- # Example:
- # company = Hubspot::Company.find(hubspot_company_id)
- # company.delete
- #
def delete
self.class.archive(id)
end
alias archive delete
def resource_name
self.class.resource_name
end
# rubocop:disable Metrics/MethodLength
-
- # getter: Check the properties and changes hashes to see if the method
- # being called is a key, and return the corresponding value
- # setter: If the method ends in "=" persist the value in the changes hash
- # (when it is different from the corresponding value in properties if set)
+ # Handle dynamic getter and setter methods with method_missing
def method_missing(method, *args)
method_name = method.to_s
# Handle setters
if method_name.end_with?('=')
@@ -468,13 +265,12 @@
end
# Fallback if the method or attribute is not found
super
end
-
# rubocop:enable Metrics/MethodLength
- # Ensure respond_to_missing? handles existing keys in the properties anc changes hashes
+ # Ensure respond_to_missing? is properly overridden
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