0

enter image description hereGoal: dynamically update another Model's properties (Tracker) from Controller (cards_controller.rb), when cards_controller is running the def update action.

Error I receive : NameError in CardsController#update, and it calls out the 2nd last line in the def update_tracker(card_attribute) :

updated_array = @tracker.instance_variable_get("#{string_tracker_column}")[Time.zone.now, @card.(eval(card_attribute.to_s))]

Perceived problem: I have everything working except that I don't know the appropriate way to 'call' the attribute of Tracker correctly, when using dynamic attributes.

The attribute of the Tracker is an array (using PG as db works fine), I want to

  1. figure out what property has been changed (works)
  2. read the corresponding property array from Tracker's model, and make a local var from it. (works I think, )
  3. push() a new array to the local var. This new array contains the datetime (of now) and, a string (with the value of the updated string of the Card) (works)
  4. updated the Tracker with the correct attribute.

With the following code from the cards_controller.rb

  • it's the if @card.deck.tracked in the update method that makes the process start

cards_controller.rb

...
    def update
        @card = Card.find(params[:id])
        if @card.deck.tracked
            detect_changes
        end
        if @card.update_attributes(card_params)
            if @card.deck.tracked
                prop_changed?
            end
            flash[:success] = "Card info updated."
            respond_to do |format|
                format.html { render 'show' }
            end
        else
            render 'edit'
        end
    end
...
private
        def detect_changes
            @changed = []
            @changed << :front if @card.front != params[:card][:front]
            @changed << :hint if @card.hint != params[:card][:hint]
            @changed << :back if @card.back != params[:card][:back]
        end

def prop_changed?
    @changed.each do |check|
        @changed.include? check
        puts "Following property has been changed : #{check}"
        update_tracker(check)
    end
end

def update_tracker(card_attribute)
    tracker_attribute =  case card_attribute
    when :front; :front_changed
    when :back; :back_changed
    when :hint; :hint_changed
    end
    string_tracker_column = tracker_attribute.to_s
    @tracker ||= Tracker.find_by(card_id: @card.id)
    updated_array = @tracker.instance_variable_get("#{string_tracker_column}")[Time.zone.now, @card.(eval(card_attribute.to_s))]
    @tracker.update_attribute(tracker_attribute, updated_array)
end

Edit: For clarity here's the app/models/tracker.rb:

class Tracker < ActiveRecord::Base
    belongs_to :card
end
Michael
  • 119
  • 1
  • 13

2 Answers2

1

From your error it seem your issue is this:

@tracker.instance_variable_get("#{string_tracker_column}")

evaluates to this after string interpolation:

 @tracker.instance_variable_get("front_changed")

which is incorrect use of instance_variable_get. It needs an @ prepended:

 @tracker.instance_variable_get("@front_changed")

Seems like using instance_variable_get is unnecessary, though, if you set attr_reader :front_changed on the Tracker model.

max pleaner
  • 26,189
  • 9
  • 66
  • 118
  • You mean using the tracker attribute (that is set to a symbol of :front_changed like so : **updated_array = @tracker.attr_reader(tracker_attribute)[Time.zone.now, @card.(eval(card_attribute.to_s))]**? – Michael Aug 22 '16 at 20:49
  • that's not how `attr_reader` works. See http://stackoverflow.com/questions/4370960/what-is-attr-accessor-in-ruby - writing `attr_accessor :front_changed` will enable to to say `@tracker.front_changed = "something"` to set the instance variable and `@tracker.front_changed` to get its value – max pleaner Aug 22 '16 at 20:53
  • Wait, this needs to be broken into 2 lines like so :`updated_array = @tracker.attr_reader(tracker_attribute); update_array.push([Time.zone.now, @card.(eval(card_attribute.to_s))])` – Michael Aug 22 '16 at 20:53
  • again, not how attr_reader works. Learn what `attr_reader`, `attr_writer`, and `attr_accessor` do. You can use `instance_variable_get` and `instance_variable_set` (they do the same thing) but I'm just saying it's more typing. – max pleaner Aug 22 '16 at 20:55
  • thanks for feedback, I'll get back once I've read up and had a chance to apply to my code. – Michael Aug 22 '16 at 20:59
1

Your use of instance_variable_get has been corrected, however this approach is destined to fail because ActiveRecord column values aren't stored as individual instance variables.

You can use

@tracker[string_column_changed]
@card[card_attribute]

To retrieve attribute values by name. If you want to get an association, use public_send. The latter is also useful if there is some accessor wrapping the column value (eg carrierwave)

Frederick Cheung
  • 83,189
  • 8
  • 152
  • 174