Rails Controller Variables and Complex Relationships - ruby-on-rails

Let's say we have the following scenario in a Rails application:
Users have many Websites, and Websites have many Simulations (likewise, Websites belong to Users, and Simulations belong to Websites).
Now the problem is, how do I display a list of all of a User's Simulations on the User Show page?
My first gut attempt was to define the following in the Users Controller:
def show
#user = User.find(params[:id])
#websites = #user.websites
#simulations = #user.websites.simulations
end
And then use <%= render #simulations %> in the Users Show Page, but that gives a NoMethodError: undefined methodsimulations' for []:Array` when I visit localhost:3000.
So how can I create a variable in my User Controller that holds all the Simulations that belong to the Websites that belong to a particular User?
Any and all help is greatly appreciated!

class User < ActiveRecord::Base
has_many :websites
has_many :simulations, :through => :websites
end
now you can use #simulations = #user.simulations and get all the user's simulations

In your user model add this method. This will produce 2 queries but still better then joining simulations in ruby code.
def websites_simulations
Simulation.where(website_id: website_ids)
end

Use Erez's answer
You asked for it - I don't know about ruby, but each website in #websites should already contain a field that has a simulation.
This is what you're doing.
1 - You take user, a single object, and get its websites. Cool. User looks something like this (in pseudocode):
Object #user
{
Array[] websites = [(Website)site1, (Website)site2];
}
Alright, that's cool. So user.websites should return an array of websites.
2 - You try to get the simulations from a website. A website probably looks like this:
Object #website
{
Array[] simulations = [(Simulation)sim1, (Simulation)sim2];
}
Hm, why doesn't it work? Well lets break down what you're doing:
#user.websites.simulations
You're taking websites, which is an array, and trying to reference a variable that belongs to a website type and not an array type. #user.websites is an array object that holds websites, not a website itself. What you want is to get #website.simulations, not websites.simulations.
So the first step is getting a website. That's easy - one way would be to try to fetch a website from your website array in the user.
#User.websites[0] <-- may not be syntactically correct; I don't know ruby.
Now if you want to get all of the websites, iterate through them using a loop and push them to a new array. Again in pseudocode:
#all_simulations = new Array();
for(#i=0;#i<count(#user.websites);#i++) //first loop through the websites
{
for(#q=0;#q<count(#user.websites[#i].simulations);#q++) //then loop through the simulations
{
#all_simulations.push(#user.websites[#i].simulations[#q]); //push the simulation into #all_websites
}
}
What we do here is we go into each website in the user.websites array and then grab each simulation from that website and throw that into our #all_simulations variable. If you understand the concept, you should be able to convert this logic into valid ruby.

Related

How do I generate all new records for a given model randomly using pre-defined options?

I'd like one of my models, Stones, to be generated at random using pre-defined options I've stored in a set of arrays and hashes. Instead of Create using params from the URL, I'd like new Stones to always be defined using this random generation process. I don't need any user input at all, except that each stone belongs to a given player.
I'm still new to rails; where should I put all this code? I know enough to be able to define the arrays and hashes and randomly select from them when I need to, but I'm not sure where and how to replace the part of the code that draws params from URLs and fills in a new record before it is saved. I know controllers are supposed to be skinny, so do I do this in the model?
Apologies if this is a duplicate. I searched extensively and couldn't find an applicable solution.
Thanks for any help!
I would create a service for this. Something like:
# app/services/stone_creator.rb
class RandomStoneCreator
RANDOM_FOOS = ['bar', 'baz', 'bat']
def self.call(user)
Stone.create!({
foo: RANDOM_FOOS.sample,
user: user
})
end
end
And then anywhere that you need a new random stone you can call it like:
random_stone = RandomStoneCreator.call(current_user)

Creating Rails todo list app with goals and tasks on one page - missing params

I'm a Rails beginner, and have been reading tutorials and typing out applications for a few months now. I'm really enjoying it after a few years spent in the front end world, and beginning to get up to speed with it all. The time has come though for me to start building my own stuff without any handholding. So far, so good.
I'm creating a basic to-do list app, where goals and tasks are displayed on the same page - goals#index. My issue is that I'm not sure how to get all tasks for a particular goal (that belongs to a user). I understand that I need to pass an ID param to the Goal model in order to find out its tasks, like so:
Goal.find(1).tasks
The above works fine, as I've already set up foreign keys on the tasks table and have a has_many :tasks relationship for the Goal model and a belongs_to relationship for the Task model.
Here's my Goals controller:
def index
#user = current_user
#goals = #user.goals # list all goals for the current user and assign it to the #goals variable.
# Need to find all tasks for each goal ID and assign it to the #tasks variable. Goal ID needs to be supplied here, but it isn't as we're not in show action.
#tasks = Goal.find(1).tasks
As I said, I can find all tasks for a Goal when I manually enter the ID (1 in this example). This works fine in my app, no errors. But obviously I want to supply these IDs dynamically, and I'm just not sure how I get the params in there.
I have tried the below:
#tasks = Goal.find(params[:id]).tasks
and
#tasks = Goal.find(params[:goal_id]).tasks
And I get the "Couldn't find Goal without an ID" error when I try to iterate over #tasks in my view. Which makes sense, as I don't think the goal params are being passed to it as we're not in the Show action.
Surely there must be an easy Rails way?! I'm stumped and don't really know where to look. Thanks for your help and Happy New Year.
You are getting current user's goals, so when you will do this you have one array object. so when you will pass array object to find, it will have multiple IDS. so when need to find All the tasks from all goals you just need to pass Array of IDS instead of single value.
#tasks = Task.where(:goal_id => #goals)
This will run this SQL query.
SELECT "tasks".* FROM "tasks" WHERE "tasks"."goal_id" IN (SELECT
"goals"."id" FROM "goals")
So when you are dealing with array just pass ids. for e.g. [1,2,3]
Once you do #goals = #user.goals (assuming that's working, which it sounds like it is), you have your goals and there is no reason to go back to the DB to "find" them.
To get ALL your tasks from ALL of user's goals, you can do the following:
#tasks = []
#goals.each do |goal|
#tasks << goal.tasks
end
Ah of course, #goals is an array of the user's goals so I can just work with that. So simple when someone just tells you. Thanks for all your help!
Here's my final code that works (I left the controller unchanged). This gets the first goal in the array and then gets the tasks associated with each goal. I have a set number of goals so I can just use goals[0], goals[1] or goals[2] for each goal.
<% #goals[0].tasks.each do |task| %>
<li><div class="task-item"><%= task.task_name %></div></li>
<% end %>

Design - How to structure filters taking advantage of OOP in Rails API app

My app shows items in a feed. This items are ratings made by different users. Ratings can have a value, a review, etc.
Usual case is logged in user asking for feed (/users/feed). The controller takes the action:
def feed
if authenticate_with_token
imos_and_cursor = Feed.new(RandomItemFeeder.new(params[:cursor], #current_user)).feed
render json: {cursor: imos_and_cursor[imos_and_cursor.length-1], imos: imos_and_cursor[0..imos_and_cursor.length-2]}
end
end
Feed is the boss here. It controles what is served (it serves to respond with items but it also will know how to respond for feeding the people call (index of users basically).
Here are some of the feeders I have:
FriendsFeeder
RandomItemsFeeder
MostRecentItemsFeeder
Following is RandomItemFeeder, responsible of feed with random items:
class RandomItemFeeder < Feeder
def feed
influencers_ids = User.influencers.distinct(:id)
friends = User.friends(#watching_user).distinct(:id) if #watching_user
source_users = influencers_ids.concat(friends)
Rating.random_from_influencers(#watching_user, source_users).scroll(#current_cursor) do |rating, cursor|
#next_cursor = cursor
#feed << ImoPresenter.new(Imo.new(rating), #watching_user)
end
#feed << #next_cursor.to_s
end
end
Presenters
I've structured the rendering of jsons with presenters, so I have different presenters for different cases (feed, user profile, etc.).
Now, I want to incorporate filters. For example, I want that RandomItemFeeder feeds with just items of last 5 five years (obviously, Item model has corresponding fields).
The question is how can I incorporate this filtering utilizing best OOP practices. At the end, is just a scope and i can implement it in different ways, but I just want to do it right now so that I don't have to come back and refactor everything.
Thanks in advance.

Rails named scopes

I was trying to refactoring and optimizing me code. In particular, I wanted to reduce the amount of queries going to the database. In my users controller it worked very well but in an other controller, where I tried the same, it didn't. I've searched for some time now for the answer why it didn't work but I can't really answer it.
I've got users, which can subscribe to courses through enrolments. They are connected through has_many :through etc. relationships. The following works:
#users_courses = current_user.courses
#courses = #users_courses.a_named_scope
But in my courses controller the following wont work:
#all_courses = Course.all
#specific_course = #all_courses.specific_course_scope
The scopes are defined in the respective models and work properly. They are not complicated, just "where ... true/false" definitions. Does someone know the problem here? Thanks!
I'm using rails version 3.2 and ruby version 2.
Until Rails 4 you should use scoped method if you want to have ActiveRecord::Relation instance (on which you can call other scopes) returned instead of Array:
#all_courses = Course.scoped
#specific_course = #all_courses.specific_course_scoped
This should work.
If you want to use includes(:courses), you just do it, for example with:
#specific_course = #all_courses.specific_course_scoped.includes(:courses)

how to store facebook friends to DB (newbie)

i'm creating a facebook-app for university project and i'm trying to store all my friends in the DB.
By using the API-syntax "me/friends" i get a facebook-respond looking like this:
{"data"=>[{"name"=>"Albert Einstein", "id"=>"11111111"}, {"name"=>"Max Mustermann", "id"=>"222222222"}, {"name"=>"Just Another Name", "id"=>"333333333"}]}
I believe its a json-object, but i'm not sure.
Question: How can i save the data, i need a DB with all the User-IDs of my friends.
Thx!
Edit:
Hey, this is what i have searched for. But i still get an error and don't know why.
My code:
def insert_1
fb_friends = rest_graph.get('me/friends')
fb_friends[:data].each do |f|
#friend = Model.new(:name => f["name"] )
#friend.save
end
end
I get an Heroku error (We're sorry, but something went wrong.)
You have two options -
Option 1-
You can create a friends table which will belong to users table. If a user has 200 friends, it will create 200 entries in friends table all belonging to the user via has_many-belongs_to relationship. For storing data, you just have to iterate over facebook friends hash and then save each of them separately
Pros : You can search for any friend separately.
Cons : There will be so many of friend entries. Saving them will take time, if somebody has many friends(say 500-700). Repeating entries will be created for mutual friends.
Options 2
You can add a friends column in your users table and declare this in your user.rb
serialize :friends
This way, you just have to pass a hash object to friends attribute of user table, and rails will save that in yaml format for you. When you will do #user.friends, rails will again convert that yaml formatted data to hash object and return it.
Pros : There will be only one query to save all users. You can iterate through this hash to show list of all friends.
Cons : You can't update them separately, you will update all together. Not good if you want to store some other information in relation to user's friends.
Update
as per your code example above
fb_friends = #your logic to get data as shown above.
fb_friends[:data].each do |f|
#friend = Friend.new(:name => f["name"],:fb_user_id => f["id"] )#creating Friend model obj.
#friend.save
end

Resources