0

I have seen quite a few posts on this topic and apologise for not being able to use them for my example but i can seem to get it to work

I have a form that can be pre populated with the users information if the data exisits

def new
  BraintreeTransaction::PopulateForm.new(@user).populate_form
end

module BraintreeTransaction
  class PopulateForm
    def initialize(user)
      @user = user
    end

    def populate_form
     return if Transaction.where(user_id: @user.id, completed: false).empty?
     user_details = Transaction.where(user_id: @user.id, completed: false).order(created_at: :desc).first
     @first_name = user_details.first_name if user_details.first_name.present?
     @last_name = user_details.last_name if user_details.last_name.present?
   end
end

My Form

<%= text_field_tag :first_name, @first_name, placeholder: 'First Name', required: true %>
<%= text_field_tag :last_name, @last_name, placeholder: 'Last Name', required: true %>

How do i access the instance variable in this situation?

Thanks

5
  • You mean @user? You need and accessor, like attr_reader :user (or manually define def user; @user end) Commented Feb 17, 2016 at 14:42
  • could you explain why @user ? Commented Feb 17, 2016 at 14:50
  • Your title mentions accessing a "class instance variable", and @user is an instance variable, so I supposed you were asking how to access it outside of PopulateForm. If I made the wrong assumption let me know. Commented Feb 17, 2016 at 14:53
  • 1
    By the way, instance variable and class instance variable are different things. Simplifying things a bit, the latter is a variable of a Class instance (i.e., of some class), while the former is a variable of an Object instance. Commented Feb 17, 2016 at 14:57
  • There are no class instance variables in the code you posted. Could you please make sure to post the actual code corresponding to your question? Otherwise it is impossible to answer the question. Commented Feb 17, 2016 at 15:51

1 Answer 1

2

The simple answer here is to add attr_reader which makes these variables accessible:

class BraintreeTransaction::PopulateForm
  attr_reader :first_name
  attr_reader :last_name

  # ...
end

Then you can access these only if you capture the populate module:

def new
  @populate = BraintreeTransaction::PopulateForm.new(@user).populate_form
end

In your view you can then use them like this:

<%= text_field_tag :first_name, @populate.first_name, ... %>

I'd recommend doing this lazy-loading style though, to avoid having to manually initialize it:

class BraintreeTransaction::PopulateForm
  def initialize(user)
    @user = user
  end

  def first_name
    user_details and user_details.first_name
  end

  def last_name
    user_details and user_details.last_name
  end

  def user_details
    @user_details ||=
      Transaction.where(
        user_id: @user.id,
        completed: false
      ).order(
        created_at: :desc
      ).first
end

This has the effect of cutting your queries in half and caching the result so only the first access has cost. Calling populate_form manually is implicit, no longer required.

Sign up to request clarification or add additional context in comments.

9 Comments

wow that's a great answer and really simple to follow, thank you
would you mind explaining the user_details method please, mainly the @user_details ||= thanks
also i've noticed you namespaced the class and not have module > class.. is this a better practice? do i not need to declare a module ?
I've just collapsed the module X class Y definition into class X::Y for brevity here. Generally if you have a module X definition in x.rb and class Y in x/y.rb then the autoloader will figure it out. I find the less nesting you do the better, so I tend to use the shorter method.
The Ruby idiom @x ||= y only initializes @x with a value if @x is a non-true boolean value, that is nil or false. It's often used as a "lazy initializer", or one that's generally called once and only once. This one is a little too lazy, you may end up running that query several times if there's no match, but you can make it less lazy with a bit more work. The example here does the job, though. x ||= y is equivalent to x = x || y or more specifically x = y unless x.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.