Filtering Model Object Data

1 view
Skip to first unread message

BenL

unread,
Mar 14, 2008, 9:28:28 PM3/14/08
to Streamlined
Hi There,

I'm very new to this streamlined plugin - and I must say I am really
loving it so far.

I have a need to filter model data before it gets rendered.

The example is - I have users who log in. They should only be able to
see the records that are relevant to them. It is a multi-user task
tracking system, and you only want to see "your" tasks - not
everyone's tasks.

Normally I would implement this using ":conditions=>model.user_id =
current_user.id" or something like that.

Is there a method in streamlined I can use to pass through these
conditions so the irrelevant records are filtered out in the
model.find(:all, @options) part of the crud_methods.rb

Thanks,

Ben

Matthew Bass

unread,
Mar 15, 2008, 11:28:50 AM3/15/08
to strea...@googlegroups.com
You can use a scoping statement in an around filter to achieve this. In your Streamlined-enabled controller, you would add something like this:

 around_filter :scope_by_user, :only => [:list, :index]

def scope_by_user
  Task.find_with_user_scope(current_user) { yield }
end

The only thing to keep in mind here is that this will scope ALL calls to Task#find within your action and associated helpers. So, for example, if you were displaying the total number of tasks in the system via a helper, adding this scoping statement would instead display the total number of tasks *for that user.* This might be exactly what you want, but if it's not you could eliminate the scoping this way:

with_exclusive_scope { code_that_should_not_be_scoped }

 
HTH,
Matthew

BenL

unread,
Mar 15, 2008, 4:03:45 PM3/15/08
to Streamlined
Hi Matthew,

That sounds exactly like what I want. In essence I do want to have
a :conditions parameter on all Task.find methods.

I'll try it out today and see how I go.

Thanks for your help.

~Ben

BenL

unread,
Mar 15, 2008, 6:39:51 PM3/15/08
to Streamlined
Ok,

I should have mentioned - I am running rails 2.0.2 and hence running
Streamlined edge.

I tried the with_scope method - albeit in a slightly different way due
to with_scope being protected. This means you can only set scope from
the model.
I tried a few things, and I couldn't seem to get the scope to execute
correctly.

Here is the example I used.

Model: Thought
DB Schema (so you know the model fields)
create_table "thoughts", :force => true do |t|
t.string "value"
t.datetime "date"
t.datetime "created_on"
t.datetime "modified_on"
t.integer "user_id"
t.integer "position"
end

In the Thought_Controller I tried

around_filter :scope_by_user, :only => [:list, :index]

def scope_by_user
Thought.with_scope(:find=>{:conditions=>"user_id=1"}) { yield }
end

And got error: protected method `with_scope' called for #<Class:
0x25a0378>
This seems to be because with_scope is protected in rails 2.0.2

So then i edited crud_methods.rb and I changed
models = model.find(:all, @options)
to
models = model.find_scoped(:all, @options) for testing.

Then in the thought model:
def self.find_scoped(*args)
with_scope(:find=>{:conditions=>"user_id=1"}) do
find(*args)
end
end

Even with this - the scope still does not apply and I see other user
ID's in the view.

Any Ideas?

Thanks,
Ben

BenL

unread,
Mar 17, 2008, 2:40:30 AM3/17/08
to Streamlined
Ok so i have come up with a solution to this.
I'm still not sure about how well this fits design wise - feel free to
contribute your thoughts - i'll think about it over time and see if it
could be done better.

What I have done is in the generic controller (crud_methods.rb) I have
noticed the @options parameter - so I have created the ability to grab
some conditionals (or any other find parameter for that matter) from
the specific controller as defined.

In /vendor/plugins/streamlined/lib/controller/crud_methods.rb
I have added the following line:
@options.smart_merge!(model_filter) unless !self.respond_to?
(:model_filter)

after:
@options.smart_merge!(filter_options)

This essentially calls the model_filter method of the specific
controller.
In my case, in /app/controllers/thought_controller.rb
I have created the method
def model_filter
{:conditions=>{:user_id=>current_user.id.to_s}}
end

You can basically put whatever you like in this hash and it will be
passed through to the model.find() method (by the generic controller).

I'm keen to see what you guys think about this solution (design wise)
- whether its good, bad, ugly or otherwise.

Regards,
~Ben

Craig Buchek

unread,
Mar 19, 2008, 10:19:42 PM3/19/08
to Streamlined
I'd be a bigger fan of putting it in the acts_as_streamlined call, as
an option. So I'd like to do something like:

acts_as_streamlined :model => Product, :conditions => {:user_id =>
current_user.id}

But it doesn't look like that's the "Streamlined way" of doing things.
There's a method called streamlined_model to set the model name. So
you should probably rename your method to streamlined_conditions or
streamlined_model_filter.

acts_as_streamlined
streamlined_model Product
streamlined_conditions {:user_id => current_user.id}

You'd define the streamlined_conditions method in the streamlined/
controller.rb file. Note that there are already some filtering methods
in that file, dealing with render filters and database filters. You
might want to work with those filters, and store the model filter in
filters[:model].

Craig Buchek

Matthew Bass

unread,
Mar 21, 2008, 6:12:58 PM3/21/08
to strea...@googlegroups.com
Ben, did you try restarting your server after editing crud_methods.rb? Streamlined doesn't auto-reload its classes in development mode so a restart is required anytime a change is made.

The with_scope methods seems cleaner than anything else I've tried, but if you found a different way to get it to work then that's cool. If you're still wanting to use with_scope, try the restart and post again here with the results. I'll try to duplicate the problem on my end if a restart doesn't solve it for you.

Thanks,
Matthew

BenL

unread,
Mar 24, 2008, 1:35:35 AM3/24/08
to Streamlined
Hi Matthew,

I'm sure I did reboot the server when I tried it last time.
If you would like me to test it there is a couple of things I'd like
to be clear on (at risk of sounding stupid).

with_scope is now protected in Rails 2.0 - so it can't be called
directly from the controller.
I am a bit confused how I now apply a scope from the controller.

I want to be sure I am implementing in the way you are intending. My
setup of adding to the @options array is working great - however I am
definitely keen to try your solution.
Could you send me an example method call structure - i.e

around_filter :controller_method

def controller_method
#Somehow sets with_scope on the model.
end

~Ben

Matthew Bass

unread,
Mar 24, 2008, 5:57:13 PM3/24/08
to strea...@googlegroups.com
If you're comfortable with your solution than feel free to stick with it. I dislike having to modify an external plugin or gem to achieve what I want to do, even if the modification is required due to a weakness in the framework (which this is).

This is the way I've used with_scope in the past...

MODEL:

class Order < ActiveRecord::Base
  ...
  def self.find_with_state_scope(state, &block)
    with_scope(:find => { :conditions => ["state = ?", state] }) { yield }
  end
end

CONTROLLER:

class FooController < ApplicationController
  ...
  around_filter :scope_by_state, :only => [:list, :index]

 
  def scope_by_state
    scope = params[:scope] ? params[:scope] : "pending"
    Order.find_with_state_scope(scope) { yield }
  end
end

Reply all
Reply to author
Forward
0 new messages