airrecord icon indicating copy to clipboard operation
airrecord copied to clipboard

Problem with client association on create

Open jjercx opened this issue 3 years ago • 1 comments

I was unable to create a record in my Sale model. Then I found that it's because I have a relationship named client and I think it's collisioning with Airrecord::Table.client.

This is the problem:

class Sale < Airrecord::Table
  self.base_key = ENV['AIRTABLE_BASE_ID']
  self.table_name = 'Venta'

  belongs_to :client, class: "Client", column: "Cliente"
end

Sale.create('Notas' =>'hola')
NoMethodError: undefined method `connection' for nil:NilClass
from /Users/juanjo/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/airrecord-1.0.7/lib/airrecord/table.rb:153:in `create'

But it works if I comment the relationship:

class Sale < Airrecord::Table
  self.base_key = ENV['AIRTABLE_BASE_ID']
  self.table_name = 'Venta'

  # belongs_to :client, class: "Client", column: "Cliente"
end

Sale.create('Notas' =>'hola')
=> #<Sale:0x00007f8e4d5069b8
 @created_at=2021-08-12 02:30:29 UTC,
 @fields={"ID"=>183, "Notas"=>"hola", "Creado en"=>"2021-08-12T02:30:29.000Z", "Subtotal"=>0, "Total"=>0, "Total despues comisiones"=>0, "Total Efectivo"=>0, "Total Bancos"=>0},
 @id="rec3psj5RFUhd5Zhg",
 @updated_keys=[]>

Is this the actual error? What are the alternatives for a fix? (I work on a solution with guidance, would be my first time) Can we document client is reserved?

jjercx avatar Aug 12 '21 02:08 jjercx

yes, your belongs_to is clashing with Airrecord client class method. Probably it would be better to raise a warning message or an exception when defining the belongs_to method here, if an Airrecord method is being overriden:

    def has_many(method_name, options)
      safely_define_method(method_name.to_sym) do
        # Get association ids in reverse order, because Airtable's UI and API
        # sort associations in opposite directions. We want to match the UI.
        ids = (self[options.fetch(:column)] || []).reverse
        table = Kernel.const_get(options.fetch(:class))
        return table.find_many(ids) unless options[:single]

        (id = ids.first) ? table.find(id) : nil
      end

      safely_define_method("#{method_name}=".to_sym) do |value|
        self[options.fetch(:column)] = Array(value).map(&:id).reverse
      end
    end

    def safely_define_method(method_name, &block)
      # Warn if method exists
      if method_defined?(method_name)
          warn "#{self.name}: overriding method '#{method_name}'!"
        end
      end

     send(:define_method, method_name, &block)
    end

Note I haven't tested this implementation. What do you think @sirupsen ?

pjmartorell avatar Feb 26 '22 13:02 pjmartorell