On Wednesday 31 May 2006 5:28 am, Daniel wrote:

> I have three Models
>
> User, Group and File.
>
> User habtm group
> Group habtm File
>
> How can I find all that files that a particular user has access to?
> eg user.groups.files ?

Step back from the problem a step.  Forget that you are working with models or 
that you are dealing with habtm relationships in Rails.

user.groups returns an array.  And each of the elements in that array, if you 
call files() on it, returns another array.

So all you are really looking at here is a requirement to iterate over an 
array of arrays, creating a set consisting of each of the unique values from 
those arrays.  Strip all the Rails stuff away, and it's just a simple data 
problem.

So, create a simple surrogate to think about:

a = [[1,2,3],
  [2,3,4],
  [3,4,5],
  [4,5,6],
  [5,6,7],
  [6,7,8]]

That's just an array of arrays.  Arrays in Ruby provide some handy methods for 
treating them like sets.  And what you want is the union of all of your sets.
With an array, | performs a union operation.  So:

[1.,2,3] | [2,3,4]

returns

[1,2,3,4]

So all that you need to do now is iterate through your array applying each sub 
array to a common array with the union operator.

arr = []
a.each {|elem| arr = arr | elem}

That works.  You get:

[1,2,3,4,5,6,7,8]

FYI, Ruby also provides a method, inject(), that is perfect for this kind of 
operation.  So, the above code can be rewritten like so:

a.inject([]) {|arr,elem| arr | elem}

inject() just takes a starting value and a block.  The arguments to the block 
are an accumulator variable, which is initialized to the starting value 
passed to inject, and each successive element of the array inject it 
iterating over.  The code in the block is evaluated and the result of the 
code is inserted into the accumulator variable.  When all values of the array 
have been iterated over, inject() returns the final value of the accumulator.  
It's the loop for code that needs to accumulate values.

Anyway, that's the way to solve the problem with an arbitrary set of arrays.  
So, now just apply it to your model.

Something like this, probably:

class User
  def files
    groups.inject([]) {|all_files, some_files|  all_files | some_files}
  end
end

Hope this helps,


Kirk Haines