Creating a ruby object from multiple models - ruby-on-rails

I am trying to create an object from several models which I can return using an active record query. The aim being to use the results to create a CSV output file from a subsequent query.
Lets say I have a simple model of items which have a status. The status is held with the ID as you would do in a normalised database. An item would have many statuses and a status would belong to an item (to achieve the relationship).
I've put some data in to illustrate this:
Item (model)
============
id status_id
---------------
1 1
2 2
3 3
Status
============
id title
---------------
1 Available
2 Loaned
3 Missing
Basically I want to be able to return the data as follows in an object:
item_id status_title
--------------------
1 Available
2 Loaned
3 Missing
I know I could achieve this using a "find by sql", however I was hoping this could be returned using active record and relationships.
I've had a look at trying to use delegate but couldn't get that to work (assuming that it would do what I need).
Anyone advise on if this is possible?

You should declare belongs_to association:
class Item
belongs_to :status
end
and use it in AR query:
Item.joins(:status).select('items.id as id, statuses.title as status_title')

Related

Return only results based on current object for dynamic menus

If I have an object that has_many - how would I go about getting back only the results that are related to the original results related ids?
Example:
tier_tbl
| id | name
1 low
2 med
3 high
randomdata_tbl
| id | tier_id | name
1 1 xxx
2 1 yyy
3 2 zzz
I would like to build a query that returns only, in the case of the above example, rows 1 and 2 from tier_tbl, because only 1 and 2 exist in the tier_id data.
Im new to activerecord, and without a loop, don't know a good way of doing this. Does rails allow for this kind of query building in an easier way?
The reasoning behind this is so that I can list only menu items that relate to the specific object I am dealing with. If the object i am dealing with has only the items contained in randomdata_tbl, there is no reason to display the 3rd tier name. So i'd like to omit it completely. I need to go this direction because of the way the models are set up. The example im dealing with is slightly more complicated.
Thanks
Lets call your first table tiers and second table randoms
If tier has many randoms and you want to find all tiers whoes id present in table randoms, you can do it that way:
# database query only
Tier.joins(:randoms).uniq
or
# with some ruby code
Tier.select{ |t| t.randoms.any? }

Tracking Followers Over Time

I want to build functionality in my Rails application that shows follower trends over time.
Currently, my following methodology involves creating and destroying relationship objects - following creates an object with the IDs of the follower and followed and unfollowing deletes that relationship object.
Since the relationship object is deleted upon an unfollow, it's impossible to go back and look at how many followers existed for a followed at any given time.
To solve this, the best solution I can think of is this:
Instead of deleting a relationship object upon unfollowing, create a new object with a negative value of, say, -1. Following would create an object with a positive value of +1. Therefore, adding up the total values for a given pair would yield whether or not they were currently following (1 or 0), while historical trends could also be calculated by adding up the total following values for a given followed.
My question is: Is this the most elegant solution this problem? Is there an easier way to do it? I realize that it's possible to use cron jobs to output a daily number, but that seems like it would duplicate data. Any other suggestions?
Any help would be greatly appreciated!
I would add an active field then instead of deleting the relationship record I would set the record to inactive. Then you'll have to update all of your user facing queries to reflect active = 1. Then you can use the records with active = 0 for reporting purposes. You can also add a deactivated_at field that stores the date that the record was deactivated.
An example scenario would be user 1 follows user 2, follows user 3, follows user 4, un-follows user 2, re-follows user 2, un-follows user 4.
follower_id followed_id active created_at deactivated_at
1 2 0 9/10/2012 9/13/2012
1 3 1 9/10/2012 NULL
1 4 0 9/10/2012 9/17/2012
1 2 1 9/16/2012 NULL
just use paranoia
https://github.com/radar/paranoia
class Relationship < ActiveRecord::Base
acts_as_paranoid
...
end
(if you have a unique index over the two numeric ID columns, remove it, use a plain index)
Then you can have
def currently_following_count(uid)
Relationship.where(:followed_id => uid).count
end
def historical_following_count(uid)
Relationship.unscoped.where(:followed_id => uid).count
end

How do I find where id does not match any of an array?

I have a Workout model that has and belongs to many Equipment models. I have an array of some Equipment IDs. I want to find all Workouts that don't have any Equipment assigned that matches any of the array of Equipment IDs.
So, if my array = [2,3,5] I want to find all workouts where the assigned equipment ids does not include 2, 3 or 5.
EDIT:
Workout.joins(:equipment).where("equipment.id not in(?)",[2,3,5]).uniq
Assuming five instances of Equipment, the code above returns workouts with equipment.ids 1 and 4 (good), but also returns partial matches for example Workouts with equipment.id = [1,2], [1,2,3].
It helps to think of what result set your query returns.
Workout.joins(:equipment).where("equipment.id not in(?)",[2,3,5]).uniq
Joins all the related equipments to their workouts. If a workout was linked to 4 equipments then you'd get 4 rows for that workout. The where clause just filters that 4 down to a smaller number - it can't wipe them all out just because one matches.
What you need to do instead is add conditions to the join itself. Something like
select workouts.*
left join equipments_workouts on workout_id = workouts.id and equipment_id in (2,3,5)
where equipment_id is null
Should return the correct workouts (it should also return a workout with 0 equipments but I don't know if that's a consideration.)
This works by trying to join 'bad' equipments. Because it's a left join, if no such row can be found then the result set will still include a row for that workout but with the columns for equipmnts_workouts all set to null. As a bonus you no longer have to eliminate duplicates.
Activerecord doesn't have a very nice way of writing queries like this. The joins method will accept an arbitrary SQL fragment though:
Workout.joins("left join equipment_workouts on workout_id = workouts.id and equipment_id in (2,3,5)").
where("equipment_id is null")
You might find the sanitize_sql method useful for generating that sql fragment
Workout.joins(:equipment).merge(Equipment.where("id not in(?)",[2,3,5])).uniq
or
Workout.joins(:equipment).where("equipments.id not in(?)",[2,3,5]).uniq
also u can try this, it should find all Workouts that don't have any Equipment
Workout.includes(:equipment).where("equipments.id not in(?)",[2,3,5])
This can be improved, but should work:
class Workout < ActiveRecord::Base
scope :without_equipments, lambda{|ids| joins(:equipment).where("equipments.id not in (?)", ids.repeated_permutation(ids.size).map(&:uniq).uniq)}
end
Workout.without_equipments 2,3,5

How can i show only the results that have an association in rails 3?

I am trying do a where statement that only brings up results that have that particular association.
For example:
A company has many statuses through company statuses. They can have multiple statuses that can be gold, silver, and/or bronze, or none at all. I am trying to have my results only return the companies that have a status (gold, silver, and/or bronze) and not the ones have no statuses.
Your use case isn't very well articulated, but I think what you're wanting is to find Companies that have a status that matches a specified one:
Company.includes(:statuses).where('status.name = ?', params[:status_name])
That should give you the right query, assuming a Company has_many :statuses.
From the Ruby on Rails guide on Active Record Associations:
4.2.3 How To Know Whether There’s an Associated Object?
To know whether there’s and associated object just check
association.nil?:
if #supplier.account.nil?
#msg = "No account found for this supplier"
end
http://guides.rubyonrails.org/association_basics.html#detailed-association-reference
Company.joins(:statuses).select("DISTINCT(companies.id), companies.*, statuses.*")
If you have an associative table called companies_statuses:
to retrieve all companies with at least one status
Company.where("EXISTS (select 1 from companies_statuses where companies_statuses.company_id = companies.id)")
to retrieve all companies with no status
Company.where("NOT EXISTS (select 1 from companies_statuses where companies_statuses.company_id = companies.id)")

How to discover associated model attributes from each result in Sphinx result set? (Rails, ThinkingSphinx)

I have the following Rails models:
ad
category
ad belongs to category
When I perform a search for an advert only the adverts title is searched for a match, I want to be able to find out how many ads in the result set belong to each category so that I can then generate a list of category names showing how many matching ads there are in each.
I have no idea how to extract the category names & number of ads within each category from the sphinx result set, can you help?
The best approach for getting a summary of the number of ads per category would be to add an attribute of the category_id to your Ad index definition, and flag it as a facet as well.
has category_id, :facet => true
Then, after running rake ts:rebuild so Sphinx is aware of your changes, run a facet search:
Ad.facets[:category_id] # => {1 => 10, 2 => 4}
The extra step that you'll need to do yourself is to translate each category id (the keys of the resulting hash) into a Category model/name.

Resources