Intercepting creation of new object - ruby-on-rails

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

Related

Rails - Updating Boolean Attribute in a model on Create

I'm creating an app that lets users purchase items from an online store. I followed the RailsCasts episodes, and built my OrdersController like so.
def create
#order = current_cart.build_order(order_params)
#order.ip_address = request.remote_ip
if #order.save
if #order.purchase
Item.where(email: Order.last.email).last.purchased == true
PurchaseMailer.confirmation_email(Item.last.email).deliver
flash[:notice] = "Thanks for your purchase"
redirect_to root_path
else
flash[:danger] = "Something was wrong"
redirect_to :back
end
else
render :action => 'new'
end
end
I recently decided to add an attribute to my items, which says whether or not they've been purchased or not. Items in the cart have not yet been purchased. I created a migration, giving all items a purchased attribute, that is a boolean.
By default, items are not purchased, so the default value is false.
class AddPurchasedToItem < ActiveRecord::Migration
def change
add_column :items, :purchased, :boolean, :default => false
end
end
That's why I added this line of code to my Orders#Create action.
Item.where(email: Order.last.email).last.purchased == true
Here I was setting the value of purchased from false to true. However, when I load up rails console
Item.last.purchased
=> false
It looks like the value still isn't being stored
As another response points out, you're using the == to assign a value, which isn't right. You need = instead.
And you have to save an item after you assign a value to it.
An example:
conditions = {email: Order.last.email} # using your conditions
item = Item.find_by(conditions)
item.purchased = true
item.save # this is what you're missing
Item.find(item.id).purchased # will be true
Another way to update is the following:
item.update_attribute(:purchased, true)
Yet another way is to call update_all on the ActiveRecord::Relation object like so:
# update all items that match conditions:
Item.where(conditions).update_all(purchased: true)
Which method you choose may depend on the scenario as update_all doesn't run the callbacks you specify in the model.
In your case however, all you're missing is the item.save line.
Item.where(email: Order.last.email).last.purchased == true
You're using a == operator to try to assign a value. Try using = instead.

Merge method issue rails

Background:
I have a model called Opportunity that has the "created_by" and "team_name" attributes, which correspond to a User model that has a "full_name" and "team" attribute. Thus, when a User logs in and creates a new Opportunity record, the systems created_by = User.full_name.
Problem (except from my Opportunity controller):
def create
#opportunity = Opportunity.new(opportunity_params)
#opportunity = Opportunity.new(opportunity_params.merge(:created_by => current_user.full_name))
#opportunity = Opportunity.new(opportunity_params.merge(:team => current_user.team))
end
I use the opportunity_params.merge method twice. When this happens, only the last opportunity_params.merge line works. Right now, I use opportunity_params.merge to record current_user.team so the current_user.full_name does not record. Can anyone help?
Merge your changes before you use the params:
merged_opportunity_params = opportunity_params.merge(
created_by: current_user.full_name,
team: current_user.team
)
#opportunity = Opportunity.new(merged_opportunity_params)

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

Add value to :params[]

What i have: (Action in Controller)
def create
#test = Test.new(params[:test])
#test.save
devicefiles = params[:devicefiles]
if devicefiles != nil
devicefiles.each do |attrs|
devicenote = Testdevicenote.new(attrs, :test_id => #test.id)
devicenote.save
end
end
end
This controller action does not show any error message and is rendering the view, but :test_id is not being saved in the database. How can i solve this?
EDIT: Ok whoops, I see it now...
Models only take one hash on initialize, not 2.
Testobjectnote.new(attrs.merge(:test_id => #test.id))
In short no one here has any clue, because that's not enough information. We dont know how your models are setup.
But when debugging models that "won't save" it's often good to use the bang version save, save!. save returns true or false letting you know if it was able to save the record. But save! will raise exceptions when the model can't be saved, and the exception will tell you why.
That exception will likely tell you why the record is not being saved.
Also, its usually better to use the associations, rather than manage the ids yourself.
def create
#test = Test.new(params[:test])
if params[:devicefiles]
params[:devicefiles].each do |attrs|
#test.testdevicenotes << Testdevicenotes(attrs)
end
end
#test.save
end
It's hard to say because you didn't post your view with the form that is posting to the create action, but if it's a typical Rails form, it should probably look like:
def create
#test = Test.new(params[:test])
#test.save
devicefiles = params[:test][:devicefiles]
if devicefiles != nil
devicefiles.each do |attrs|
devicenote = Testdevicenote.new(attrs, :test_id => #test.id)
devicenote.save
end
end
objectfiles = params[:test][:objectfiles]
if objectfiles != nil
objectfiles.each do |attrs|
objectnote = Testobjectnote.new(attrs, :test_id => #test.id)
objectnote.save
end
end
end
This assumes that :devicefiles and :objectfiles are inside the form :test

How can I combine the results of different variables into one variable?

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)

Resources