Class: DhanHQ::BaseModel

Inherits:
Object
  • Object
show all
Extended by:
APIHelper, AttributeHelper, RequestHelper, ResponseHelper, ValidationHelper
Includes:
APIHelper, AttributeHelper, RequestHelper, ResponseHelper, ValidationHelper
Defined in:
lib/DhanHQ/core/base_model.rb

Overview

Base class for resource objects Handles validation, attribute mapping, and response parsing

Constant Summary

Constants included from ResponseHelper

ResponseHelper::STATUS_ERROR_FALLBACK

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from APIHelper

handle_response

Methods included from AttributeHelper

camelize_keys, inspect, normalize_keys, snake_case, titleize_keys

Methods included from ValidationHelper

validate!, validate_params!

Methods included from RequestHelper

build_from_response

Constructor Details

#initialize(attributes = {}, skip_validation: false) ⇒ BaseModel

Initialize a new resource object

Parameters:

  • attributes (Hash) (defaults to: {})

    The attributes of the resource



30
31
32
33
34
35
36
# File 'lib/DhanHQ/core/base_model.rb', line 30

def initialize(attributes = {}, skip_validation: false)
  @attributes = normalize_keys(attributes)
  @errors = {}

  validate! unless skip_validation
  assign_attributes
end

Class Attribute Details

.defined_attributesObject (readonly)

Returns the value of attribute defined_attributes.



41
42
43
# File 'lib/DhanHQ/core/base_model.rb', line 41

def defined_attributes
  @defined_attributes
end

Instance Attribute Details

#attributesObject (readonly)

Attribute Accessors



25
26
27
# File 'lib/DhanHQ/core/base_model.rb', line 25

def attributes
  @attributes
end

#errorsObject (readonly)

Attribute Accessors



25
26
27
# File 'lib/DhanHQ/core/base_model.rb', line 25

def errors
  @errors
end

Class Method Details

.allArray<DhanHQ::BaseModel>, DhanHQ::ErrorObject

Find all resources

Returns:



104
105
106
107
108
# File 'lib/DhanHQ/core/base_model.rb', line 104

def all
  response = resource.get("")

  parse_collection_response(response)
end

.apiObject

Provide a shared BaseAPI instance for this model

For child classes, override ‘api_type` or `api` if needed



63
64
65
# File 'lib/DhanHQ/core/base_model.rb', line 63

def api
  @api ||= BaseAPI.new(api_type: api_type)
end

.api_typeObject

Provide a default API type, can be overridden by child classes

e.g., def self.api_type; :data_api; end

or override the ‘api` method entirely



56
57
58
# File 'lib/DhanHQ/core/base_model.rb', line 56

def api_type
  :order_api
end

.attributes(*args) ⇒ Object

Registers the set of attributes for this model

Parameters:

  • args (Array<Symbol, String>)

    A list of attribute names



46
47
48
49
# File 'lib/DhanHQ/core/base_model.rb', line 46

def attributes(*args)
  @defined_attributes ||= []
  @defined_attributes.concat(args.map(&:to_s))
end

.create(attributes) ⇒ DhanHQ::BaseModel, DhanHQ::ErrorObject

Create a new resource

Parameters:

  • attributes (Hash)

    The attributes of the resource

Returns:



134
135
136
137
138
139
# File 'lib/DhanHQ/core/base_model.rb', line 134

def create(attributes)
  # validate_params!(attributes, validation_contract)

  response = resource.post("", params: attributes)
  build_from_response(response)
end

.find(id) ⇒ DhanHQ::BaseModel, DhanHQ::ErrorObject

Find a resource by ID

Parameters:

  • id (String)

    The ID of the resource

Returns:



114
115
116
117
118
119
# File 'lib/DhanHQ/core/base_model.rb', line 114

def find(id)
  response = resource.get("/#{id}")

  payload = response.is_a?(Array) ? response.first : response
  build_from_response(payload)
end

.parse_collection_response(response) ⇒ Array<BaseModel>

Helper method to parse a collection response into model instances

Parameters:

  • response (Object)

    The raw response from the API

Returns:



145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/DhanHQ/core/base_model.rb', line 145

def parse_collection_response(response)
  # Some endpoints return arrays, others might return a `[:data]` structure
  unless response.is_a?(Array) || (response.is_a?(Hash) && response[:data].is_a?(Array))
    DhanHQ.logger&.warn(
      "[DhanHQ::BaseModel] Unexpected response format for collection: #{response.class}. " \
      "Expected Array or Hash with :data key."
    )
    return []
  end

  collection = response.is_a?(Array) ? response : response[:data]
  collection.map { |record| new(record, skip_validation: true) }
end

.resourceObject

Returns the API resource used by collection methods.

Subclasses may override this to return a specialized API class. By default it simply returns #api.



72
73
74
# File 'lib/DhanHQ/core/base_model.rb', line 72

def resource
  api
end

.resource_pathString

Retrieve the resource path for the API

Returns:

  • (String)

    The resource path



79
80
81
# File 'lib/DhanHQ/core/base_model.rb', line 79

def resource_path
  self::HTTP_PATH
end

.validate_attributes(attributes) ⇒ Object

Validate attributes before creating a new instance

Raises:

  • (ArgumentError)


92
93
94
95
96
97
# File 'lib/DhanHQ/core/base_model.rb', line 92

def validate_attributes(attributes)
  contract = validation_contract
  result = contract.call(attributes)

  raise ArgumentError, "Validation failed: #{result.errors.to_h}" if result.failure?
end

.validation_contractnil

Default class-level validation contract — returns nil (no validation). Override in subclasses to provide a contract for class-level validation.

Returns:

  • (nil)


87
88
89
# File 'lib/DhanHQ/core/base_model.rb', line 87

def validation_contract
  nil
end

.where(params) ⇒ Array<BaseModel>, ...

Fetches records filtered by query parameters.

Parameters:

  • params (Hash)

    Query parameters supported by the API.

Returns:



125
126
127
128
# File 'lib/DhanHQ/core/base_model.rb', line 125

def where(params)
  response = resource.get("", params: params)
  build_from_response(response)
end

Instance Method Details

#assign_attributesObject

Dynamically assign attributes as methods



265
266
267
268
269
270
271
# File 'lib/DhanHQ/core/base_model.rb', line 265

def assign_attributes
  self.class.defined_attributes&.each do |attr|
    instance_variable_set(:"@#{attr}", @attributes[attr])
    define_singleton_method(attr) { instance_variable_get(:"@#{attr}") }
    define_singleton_method(attr.to_s.camelize(:lower)) { instance_variable_get(:"@#{attr}") }
  end
end

#deleteBoolean

Delete the resource

Deletes the resource from the remote API.

Returns:

  • (Boolean)

    True if deletion was successful

  • (Boolean)

    True when the server confirms deletion.



217
218
219
# File 'lib/DhanHQ/core/base_model.rb', line 217

def delete
  destroy
end

#destroyBoolean

Alias for #delete maintained for ActiveModel familiarity.

Returns:

  • (Boolean)


224
225
226
227
228
229
230
# File 'lib/DhanHQ/core/base_model.rb', line 224

def destroy
  response = self.class.resource.delete("/#{id}")
  success_response?(response)
rescue StandardError => e
  DhanHQ.logger&.error("[DhanHQ::BaseModel] Error deleting resource #{id}: #{e.class} #{e.message}")
  false
end

#idString, ...

Identifier inferred from the loaded attributes.

Returns:

  • (String, Integer, nil)


258
259
260
261
262
# File 'lib/DhanHQ/core/base_model.rb', line 258

def id
  id_value = @attributes[:id] || @attributes[:order_id] || @attributes[:security_id]
  # Convert to string for consistency, but preserve nil
  id_value&.to_s
end

#new_record?Boolean

Returns:

  • (Boolean)


236
237
238
# File 'lib/DhanHQ/core/base_model.rb', line 236

def new_record?
  !persisted?
end

#optionchain_api?Boolean

Returns:

  • (Boolean)


273
274
275
# File 'lib/DhanHQ/core/base_model.rb', line 273

def optionchain_api?
  self.class.name.include?("OptionChain")
end

#persisted?Boolean

Returns:

  • (Boolean)


232
233
234
# File 'lib/DhanHQ/core/base_model.rb', line 232

def persisted?
  !!id
end

#saveDhanHQ::BaseModel, ...

Persists the current resource by delegating to #create or #update.



187
188
189
# File 'lib/DhanHQ/core/base_model.rb', line 187

def save
  new_record? ? self.class.create(attributes) : update(attributes)
end

#save!DhanHQ::BaseModel

Same as #save but raises Error when persistence fails.

Returns:

Raises:



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/DhanHQ/core/base_model.rb', line 195

def save!
  result = save
  return result unless result == false || result.nil? || result.is_a?(DhanHQ::ErrorObject)

  error_details =
    if result.is_a?(DhanHQ::ErrorObject)
      result.errors
    elsif @errors && !@errors.empty?
      @errors
    else
      "Unknown error"
    end

  raise DhanHQ::Error, "Failed to save the record: #{error_details}"
end

#to_request_paramsHash

Format request parameters before sending to API

Auto-injects dhan_client_id from configuration if not present in attributes and the model requires it (has dhan_client_id as an attribute).

Returns:

  • (Hash)

    The camelCased attributes



246
247
248
249
250
251
252
253
# File 'lib/DhanHQ/core/base_model.rb', line 246

def to_request_params
  attrs = @attributes.dup
  # Auto-inject dhan_client_id from configuration if not provided and model supports it
  if self.class.defined_attributes&.include?("dhan_client_id") && !attrs[:dhan_client_id] && DhanHQ.configuration.client_id
    attrs[:dhan_client_id] = DhanHQ.configuration.client_id
  end
  optionchain_api? ? titleize_keys(attrs) : camelize_keys(attrs)
end

#update(attributes = {}) ⇒ DhanHQ::BaseModel, DhanHQ::ErrorObject

Update an existing resource

Parameters:

  • attributes (Hash) (defaults to: {})

    Attributes to update

Returns:



174
175
176
177
178
179
180
181
182
# File 'lib/DhanHQ/core/base_model.rb', line 174

def update(attributes = {})
  response = self.class.resource.put("/#{id}", params: attributes)

  if success_response?(response)
    self.class.build_from_response(response)
  else
    DhanHQ::ErrorObject.new(response)
  end
end

#valid?Boolean

Validate attributes using contract

Returns:

  • (Boolean)


278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/DhanHQ/core/base_model.rb', line 278

def valid?
  contract_class = validation_contract || self.class.validation_contract
  return true unless contract_class

  contract = contract_class.is_a?(Class) ? contract_class.new : contract_class
  result = contract.call(@attributes)

  if result.failure?
    @errors = result.errors.to_h
    return false
  end

  true
end

#validation_contractnil

Default validation contract — returns nil (no validation). Models that require instance-level validation must override this method.

Returns:

  • (nil)


166
167
168
# File 'lib/DhanHQ/core/base_model.rb', line 166

def validation_contract
  nil
end