0

I'm querying a mysql database and returning an array of objects created by a join. From what I understand, the ActiveRecord select command returns an array of new objects, each with the properties of both tables in the join.

I want to perform some calculations on each of these objects, and associate a few attributes with the object. I understand that if this was my class, I could use the method_missing approach as seen here. However, since this object is created by the select command, I don't have class code where I can define the add_attrs function. Is this possible? Is using method_missing the correct approach and if so, how do I implement it?

Here is the initial query which returns an array of ActiveRecord objects.

def self.select_with_location
    select("advertisements.id, advertisements.weight, locations.latitude, locations.longitude")
end

If I were to go to the ruby console and type the commands

advertisements = Advertisement.joins(:locations).select_with_location
puts advertisements[0].inspect

I should get something like:

{id: 0, weight: 5, locations.latitude: 49.03030, locations.longitude: 50.5050}

Now, what I want is to iterate through the advertisements and perform a calculation on each of the weights. I want to store the results of that calculation into a new attribute of advertisement, which I want to call weight_multiplier. After this calculation, if I go to the ruby console and type the following command:

puts advertisements[0].inspect

I would want to get the result

{id:0, weight: 5, locations.latitude: 49.03030, locations.longitude: 50.5050, weight_multiplier: 0.446003}

In this example the numbers themselves aren't important, I just want to add the weight_multiplier attribute to each of the advertisements in the array.

I know the following code is incorrect, but this is the gist of what the function will do:

def self.assign_weight_multiplier advertisements
    totalWeights = advertisements.inject(0){|sum,advertisement| sum + advertisement.weight }
    advertisements.each do |advertisement|
      advertisement.weight_multiplier = (Float(advertisement.weight)/Float(totalWeights))
    end
    advertisements
end
Community
  • 1
  • 1
Ryan McGarvey
  • 303
  • 2
  • 4
  • 14
  • 1
    While I appreciate the absence of "code vomit", where one copy-pastes several files of code willy-nilly, I'd ask you to be just a little more concrete here. Can you give us a minimal gist of your results/what you're trying to do here? Maybe a Rails console prompt would be appropriate. – Jonathan Allard Aug 13 '16 at 00:11
  • Minimal code added, I hope it's a little clearer what I'm trying to accomplish! – Ryan McGarvey Aug 13 '16 at 00:21
  • I'm afraid I still don't get it. Can you rephrase and maybe take us through your process in a Rails console? You lose me at "associate a few properties" and from then on. – Jonathan Allard Aug 13 '16 at 00:27
  • Not super familiar with the rails console, but I gave it a shot! I hope this clarifies. – Ryan McGarvey Aug 13 '16 at 00:46
  • Ahh, I think I got it. You want to have the calculations done in the aggregate instead of on each record. It helps to think of crafting your query first. Then, any columns you add to your query in select are automatically added on each of your records, if I recall correctly. Your mileage may vary, I'd be using Pry and its `ls` method liberally here to see what's available both on the relation and on the records. – Jonathan Allard Aug 13 '16 at 00:54
  • So, not exactly what I was aiming for. I do want the calculations to be done on each record. Ignore the `totalWeights = advertisements.inject(0){|sum,advertisement| sum + advertisement.weight }` line, that already works. What I want to focus on is `advertisement.weight_multiplier = `. When I do this, I get a message saying that there is no method weight_multiplier for the advertisement object. I want to define the weight_multiplier attribute for the advertisement object. Sorry if it seems like we're talking about different things, I'm farly new to Ruby. – Ryan McGarvey Aug 13 '16 at 01:08

1 Answers1

1

You can add 2 new methods to your Advertisement class

def total_weight
  @total_weight ||= Advertisement.sum(:weight)
end

def weight_multiplier
  self.weight.to_f / total_weight
end

then you can call weight_multiplier as a instance method on Advertisement instance

PGill
  • 3,373
  • 18
  • 21