Gavin
Gavin

Reputation: 81

using :scope to show results without adding a new action

I'm looking to use :scope in my rails 3.1 app and need a little direction on how to implement them correctly. I understand that you write the logic in the model (to extract the data you need), but my question comes on the next steps.

For example: I have a "Piece" of information. Each "Piece" has a Topic and Audience among other things. I'd like to use scopes to display all "Pieces" of information for a given Topic or Audience without writing new actions in my controller.

Essentially, I'm not sure how to link to these views without creating new actions in the controller and then just using the typical link_to. I'd like to use the scopes I've written.

I'm looking for the correct (and most appropriate) way to get this done.

Thanks in advance for any help on this.

Upvotes: 1

Views: 2090

Answers (2)

Adam Eberlin
Adam Eberlin

Reputation: 14205

You don't even need a scope for this. Check it out:

Piece.includes([:topic, :audience]).where(['`topics`.name = ?', 'Politics'])

With Rails 3 & Arel, most scopes are entirely unnecessary.

On using this in views without creating new controller actions, you can setup a some conditionals using params passed to your index action to determine how to accomodate the request.


For example: (and this is a rough, untested example, but it should work)

app/controllers/pieces_controller.rb:

class PieceController < ApplicationController

  def index
    case params[:find_by]
      when 'topic_name'
        @pieces = Piece.includes([:topic, :audience]).where(['`topics`.name = ?', params[:topic_name])
      when 'topic_id'
        @pieces = Piece.includes([:topic, :audience]).where(['`topics`.id = ?', params[:topic_id])
      else
        @pieces = Piece.all
    end

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @addressees }
    end
  end

  # ...
end

app/views/pieces/index.html.erb:

<%= link_to 'Politics Pieces', :controller => 'pieces', :find_by => 'topic_name', :topic_name => 'Politics' %>

Another example using a select box and javascript:

app/views/pieces/index.html.erb:

<%= select 'find_by', 'topic', find_by_topic_select_options %>

app/helpers/pieces_helper.rb: (moved link_to here for readibility)

module PieceHelper
  def find_by_topic_select_options
    Topic.all.collect do |topic|
      url = url_for :controller => 'pieces', :find_by => 'topic_id', :topic_id => topic.id
      [topic.name, url]
    end
  end
end

public/javascripts/application.js:

window.onload = (function (ev) {
  var find_by_topic_select = document.getElementById('find_by_topic');
  find_by_topic_select.onchange = (function (e) {
    var selected = this.options[this.selectedIndex];
    window.location = selected.value;
  });
});

And, a footnote, you do not link_to an association or a data structure. You link to a url or route which calls an action (which renders a view) which appropriately displays your data structure. I hope this clears things up for you.

Upvotes: 1

jbescoyez
jbescoyez

Reputation: 1393

Here is how I would do that:

In piece.rb

def self.scope_by_topic(topic_name)
  topic_name.present? ? includes(:topic).where(topic: {name: topic_name}) : self.scoped
end

def self.scope_by_audience(audience_name)
  topic_name.present? ? includes(:audience).where(audience: {name: audience_name}) : self.scoped
end

About the scoped method I use hereover: http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html#method-i-scoped

In you pieces_controller.rb:

def index
  @pieces = Piece.scope_by_topic(params[:topic_scope])
                 .scope_by_audience(params[:audience_scope]).all
  # render view here
end

And in your view, you can use the following helper:

link_to "My piece of information", pieces_path(topic_scope: 'A topic', audience_scope: 'An audience')

The URL will look like:

http://www.example.com/pieces?topic_scope=A+topic&aucience_scope=An+audience

Upvotes: 0

Related Questions