Mohamed El Mahallawy
Mohamed El Mahallawy

Reputation: 13862

Passing a variable between actions within same controller Rails

My show action:

def show
        # Multiple keywords

        if current_user.admin?
            @integration = Integration.find(params[:id])
        else            
            @integration = current_user.integrations.find(params[:id])
        end


        @q = @integration.profiles.search(search_params)
        @profiles = @q.result.where(found: true).select("profiles.*").group("profiles.id, profiles.email").includes(:integration_profiles).order("CAST( translate(meta_data -> '#{params[:sort_by]}', ',', '') AS INT) DESC NULLS LAST").page(params[:page]).per_page(20)
        @profiles = @profiles.limit(params[:limit]) if params[:limit]
end

There can be many different filters taking place in here whether with Ransacker, with the params[:limit] or others. At the end I have a subset of profiles.

Now I want to tag all these profiles that are a result of the search query.

Profiles model:

def self.tagging_profiles
  #Some code
end

I'd like to create an action within the same controller as the show that will execute the self.tagging_profiles function on the @profiles from the show action given those profiles have been filtered down.

def tagging
  @profiles.tagging_profiles
end

I want the user to be able to make a search query, have profiles in the view then if satisfied tag all of them, so there would be a need of a form

UPDATE:

This is how I got around it, don't know how clean it is but here:

def show # Multiple keywords

        if current_user.admin?
            @integration = Integration.find(params[:id])
        else            
            @integration = current_user.integrations.find(params[:id])
        end


        @q = @integration.profiles.search(search_params)
        @profiles = @q.result.where(found: true).select("profiles.*").group("profiles.id, profiles.email").includes(:integration_profiles).order("CAST( translate(meta_data -> '#{params[:sort_by]}', ',', '') AS INT) DESC NULLS LAST").page(params[:page]).per_page(20)
        @profiles = @profiles.limit(params[:limit]) if params[:limit]

        tag_profiles(params[:tag_names]) if params[:tag_names]
end

   private
   def tag_profiles(names)
     @profiles.tagging_profiles
   end

In my view, I created a form calling to self:

<%= form_tag(params.merge( :controller => "integrations", :action => "show" ), method: :get) do %>
        <%= text_field_tag :tag_names %>
        <%= submit_tag "Search", class: "btn btn-default"%>
       <% end %>

Is this the best way to do it?

Upvotes: 0

Views: 3343

Answers (2)

Richard Peck
Richard Peck

Reputation: 76774

Instance Variables

If you want to "share" variable data between controller actions, you'll want to look at the role @instance variables play.

An instance of a class means that when you send a request, you'll have access to the @instance variable as long as you're within that instance of the class, I.E:

#app/controllers/your_controller.rb
Class YourController < ApplicationController
   before_action :create_your_var

   def your_controller
      puts @var
   end

   private

   def create_your_var
       @var = "Hello World"
   end
end

This means if you wish to use the data within your controller, I would just set @instance variables, which you will then be able to access with as many different actions as you wish

--

Instance Methods

The difference will be through how you call those actions -

#app/controllers/your_controller.rb
Class YourController < ApplicationController
   def action
      #-> your request resolves here
      method #-> calls the relevant instance method
   end

   private

   def method
      #-> this can be called within the instance of the class
   end
end

Upvotes: 0

dre-hh
dre-hh

Reputation: 8044

Rails public controller actions correspond always to a http request. But here there is just no need for 2 http requests. A simple solution would be just creating to private controllers methods filter_profiles(params) and tag_profiles(profiles) and just call them sequentially.

You can also extract this problem entirely to a ServiceObject, like this:

  class ProfileTagger
    attr_reader :search_params          

    def initialize(search_params)
      @search_params = search_params
    end

    def perform
      search
      tag
    end

    def tag
     #tag found profiles
    end

    def search
     @profiles = #do the search
    end 
  end 

As processing 30,000 records is a time consuming operation, it would make sence to perform it outside of the rails request in background. This structure will allow you to delegate this operation to a sidekiq or delayed_job worker with ease

Upvotes: 0

Related Questions