How can I combine the results of different variables into one variable? - ruby-on-rails

I have following four variables in my controller index action which are retrieving data from different models as follows:
#forum = Forum.where(:user_id => #users.collect(&:user_id)).all
#poll=Poll.where(:created_by => #users.collect(&:user_id)).all
#article = Article.where(:user_id => #users.collect(&:user_id)).all
#jobpost = Jobplacement.where(:user_id => #users.collect(&:user_id)).all
I want to join all these variables' data into a single variable #post. How can I do this?

It is not good to have different type of objects in single collection.
But as you asked try
#post = [#forum,#forum,#article,#jobpost].flatten
Update:
I wrote this answer when I was a newbie in Ruby. When I look this answer I can not control my smile. The purpose of the flatten is to make a single array from the nested arrays. The answer does not relate to the question. But I am surprised about the upvotes :)

Put them in a hash:
#post = Hash.new
#post['forum'] = Forum.where(:user_id => #users.collect(&:user_id)).all
#post['poll'] = Poll.where(:created_by => #users.collect(&:user_id)).all
#post['article'] = Article.where(:user_id => #users.collect(&:user_id)).all
#post['job_placement'] = Jobplacement.where(:user_id => #users.collect(&:user_id)).all
They are not joined, but they are in one single variable. You can access them whenever you want, and do with them whatever you want.

Something like this:
conditions = { :user_id => #users } # assuming primary_key is set correctly
# in the User model
#post = Forum.where( conditions ).all +
Poll.where( conditions ).all +
Article.where( conditions ).all +
Jobplacement.where( conditions ).all
Or if you want to get fancy:
models = [ Forum, Poll, Article, Jobplacement ]
#post = models.reduce [] do |records, model|
records.push *model.where( :user_id => #users ).all
end
Note: .all might be unnecessary in both cases since it's usually called automatically by Rails when necessary, but I'm not certain.

I think you need like view model concept. Create a simple model class without inherit from ActiveRecord::Base and add all objects as attributes in the new class and initialize that.
class Post
attr_accessor :forum, :poll, :article, :jobpost
def initialize(forum,poll,article,jobpost)
#forum = forum
#poll = poll
#article = article
#jobpost = jobpost
end
end
In the controller action add the following;
#post = Post.new(#forum,#poll,#article,#jobpost)

Related

rails 3 searching and ordering by two fields

I have a list of questions with different positions and question group ids
how would i find all the questions with one id and pick out the one with the biggest position number.
is this close
<%= #question = Question.maximum('position', :conditions => {'question_group_id' => question_group_id'}) %>
using any of the answers i get
#<Question:0x3fe85c0>
how do i turn that into something we can read
First of I see an erb tag (<% %>) and the operation you perform will be better on your controller.
Now the query (in the controller) should be
#question = Question.where(question_group_id: question_group_id).
order("position desc").
limit(1). #because you only need one record!
first
You can do that with the following query.This will defiantly work for you.
#question = Question.where('question_group_id = ?',question_group_id).order("position desc").first
#question = Question.where(:question_group_id => question_group_id).order("position desc").first
First of all, I would put the query on the Model as a named scope and not in the Controller.
Given that, on your Question model:
scope :highest_position_by_question_group_id, lambda { |question_group_id|
where(question_group_id: question_group_id).order('position DESC').first
}
And on your Controller:
#question = Question.highest_position_by_question_group_id(question_group_id)

Is using Playlist.new(params[:playlist]) ever not ok?

Specifically in my new/create actions. I have #playlist = Playlist.new(params[:playlist]). The thing is I also have sensitive data in attr_accessible that I don't want them to modify (the number of listens on a playlist, which they shouldnt be able to update).
I tried Playlist.new(:title => params[:title], :description => params[:description], etc) but that didn't work. I assume because I need to do params[:playlist][:title] but this looks quite messy. Am I doing this incorrectly?
In the Model you can write a function called for example, new_safe which creates the new object with the params you want and then returns it
like this:
def new_safe(params)
playlist = Playlist.new
playlist.title = params[:title]
playlist.description = params[:description]
playlist.save
playlist
end
Just thinking, similiarly you could write it like this which is a bit cleaner
Controller:
#playlist = Playlist.new
#playlist.input_params(params)
Model:
def input_params(params)
playlist.title = params[:title]
playlist.description = params[:description]
playlist.save
end

how to give condition to loop through a variable containing data from different models

I have following four variables in my controller index action which are retrieving data from different models and i have joined them as follows:
#forum = Forum.where(:user_id => #users.collect(&:user_id)).all
#poll=Poll.where(:created_by => #users.collect(&:user_id)).all
#article = Article.where(:user_id => #users.collect(&:user_id)).all
#jobpost = Jobplacement.where(:user_id => #users.collect(&:user_id)).all
#post = #article + #jobpost + #forum + #poll
In the view i wanted to loop through #post so i wrote - #post.reverse.each do | post| but the problem is this post contains data from 4 different models and forum ,article,jobpost is having :user_id column whereas poll is having created_by as the column name for user_id field.because of this m getting the error undefined method `user_id' for # in following lines
- if User.find(post.user_id).basic_info or User.find(post.created_by).basic_info
- if User.find(post.user_id).basic_info.profilephoto?
= image_tag User.find(post.user_id).basic_info.profilephoto.url(:thumb)
how can i give condition like
- if User.find(post.user_id).basic_info or User.find(post.created_by).basic_info
or
- if User.find(post.user_id or post.created_by).basic_info
this seems like a messy thing to be doing.
but something like this should work:
user_id = post.user_id || post.created_by
if User.find(user_id).basic_info
BUT, you really shouldn't be doing database calls from a loop in your view.
It would be cleaner to do all the data collection in the controller and models.

How do I pass a var from one model's method to another?

Here is my one model..
CardSignup.rb
def credit_status_on_create
Organization.find(self.organization_id).update_credits
end
And here's my other model. As you can see what I wrote here is an incorrect way to pass the var
def update_credits
#organization = Organization.find(params[:id])
credit_count = #organization.card_signups.select { |c| c.credit_status == true}.count
end
If it can't be done by (params[:id]), what can it be done by?
Thanks!
Ideally the data accessible to the controller should be passed as parameter to model methods. So I advise you to see if it is possible to rewrite your code. But here are two possible solutions to your problem. I prefer the later approach as it is generic.
Approach 1: Declare a virtual attribute
class CardSignup
attr_accessor call_context
def call_context
#call_context || {}
end
end
In your controller code:
def create
cs = CardSignup.new(...)
cs.call_context = params
if cs.save
# success
else
# error
end
end
In your CardSignup model:
def credit_status_on_create
Organization.find(self.organization_id).update_credits(call_context)
end
Update the Organization model. Note the change to your count logic.
def update_credits
#organization = Organization.find(call_context[:id])
credit_count = #organization.card_signups.count(:conditions =>
{:credit_status => true})
end
Approach 2: Declare a thread local variable accessible to all models
Your controller code:
def create
Thread.local[:call_context] = params
cs = CardSignup.new(...)
if cs.save
# success
else
# error
end
end
Update the Organization model. Note the change to your count logic.
def update_credits
#organization = Organization.find((Thread.local[:call_context] ||{})[:id])
credit_count = #organization.card_signups.count(:conditions =>
{:credit_status => true})
end
Use an attr_accessor.
E.g.,
class << self
#myvar = "something for all instances of model"
attr_accessor :myvar
end
#myothervar = "something for initialized instances"
attr_accessor :myothervar
then you can access them as ModelName.myvar and ModelName.new.myvar respectively.
You don't say whether you're using Rails 2 or 3 but let's assume Rails 2 for this purpose (Rails 3 provides the a new DSL for constructing queries).
You could consider creating a named scope for in your Organization model as follows:
named_scope :update_credits,
lambda { |id| { :include => :card_signup, :conditions => [ "id = ? AND card_signups.credit_status = TRUE", id ] } }
And then use it as follows:
def credit_status_on_create
Organization.update_credits(self.organization_id)
end
Admittedly I don't quite understand the role of the counter in your logic but I'm sure you could craft that back into this suggestion if you adopt it.

Intercepting creation of new object

I'm adding a categorization functionality to my app and struggling with it. Objects have many categories through categorizations. I'm trying to intercept the creation of a new categorization, check if theres a similar one, if so, increment it's count, if not, create a new object. Here's what I have so far.
validate :check_unique
protected
def check_unique
categorization = Categorization.where(:category_id => self.category_id, :categorizable_id => self.categorizable_id, :categorizable_type => self.categorizable_type)
if categorization.first
categorization.first.increment(:count)
end
end
This kind of logic should not exist in the controller. This is really business domain and should be in the model. Here's how you should go about it:
categorization = Categorization.find_or_create_by_category_id_and_categorizable_id_and_categorizable_type(self.category_id, self.categorizable_id, self.categorizable_type)
categorization.increment!(:count)
find_or_create will try to find the category in the DB, and if it doesn't exist, it'll create it. Now just make sure that count defaults to zero, and this code will do what you want. (when initially created the count would be 1, then later it'll increment)
PS: I'm not sure if find_or_create has changed in rails 3. But this is the main idea
I decided to move it out of the model object and put it into the controller method creating the categorization. It now works (Yay!) and here's the code if anyone is interested.
def add_tag
object = params[:controller].classify.constantize
#item = object.find(params[:id])
#categories = Category.find(params[:category_ids])
#categories.each do |c|
categorization = #item.categorizations.find(:first, :conditions => "category_id = #{c.id}")
if categorization
categorization.increment!(:count)
else
#item.categorizations.create(:category_id => c.id, :user_id => current_user.id)
end
end
if #item.save
current_user.update_attribute(:points, current_user.points + 15) unless #item.categorizations.exists?(:user_id => current_user.id)
flash[:notice] = "Categories added"
redirect_to #item
else
flash[:notice] = "Error"
redirect_to 'categorize'
end
end

Resources