views:

61

answers:

3

Hi guys, this is a really stupid question but I have a deadline and I'm very nervous and I've tried everything. In fact it even worked for a second but I must have broken something.

I have a default featured item that I want to be overridden any time I try to feature another item. I had it give an error before but I don't want it to error, I just want it to set everything to false and this app to true.

I've tried everything, before save, after save/update named scopes, mapping each one to false and then saving, checking for whether the feature has changed...I think I have all the right parts but I can't put them together. Please please help :(

Maybe there's even a quick validation for this, I'm not sure

I can't elaborate with code because I've tried a million different things.

There is a boolean setting on an item that is called is_featured.

When you create the item, you can set it to be featured or not.

In the database, if other items are featured, these should be zeroed out, and the item that was just updated or created to be featured should be true.

I have tried this in the model with before/after/save/update and also the controler on create and update

Fine I will add some of my stupid code which Id on't know why doesn't work

In the controller:

  if params[:app][:is_on_front_page] == true
App.all.map{|m|( m != @app ) ? m.is_on_front_page = true : m.is_on_front_page = false}

end

or

  named_scope :is_on_front_page, :conditions => { :is_on_front_page => true }
    after_update do
    if self.is_on_front_page.changed? and App.is_on_front_page.count >= 1
     App.all.map{|m| m.is_on_front_page = false;m.save}

    end 
    is_on_front_page = true
    save!
   end

but it won't let me save or gets into an infinite loop or yells at me because of the named scope

+1  A: 
if params[:id]  # or however you're setting the overridden featured item
  @feature = Feature.find(params[:id])
else
  @feature = Feature.default  # make a self.default method that returns the default
end

Is that what you need? To pick a featured item?

I'm not sure exactly what you need. Validations would be for saving something, so I'm not sure what would be 'featured' there. A comment for clarification would help.

A before_create or before_update hook would be where you would set defaults if something wasn't provided, but beyond that I'd need a little guidance as to your specifics.

wesgarrison
Say I have 10 items, and I want only one to be featured. Maybe item 2 is already featured. When I go to create a new item, I want all items (really just item 2, but for now there was more than one featured so we'll keep it at that) to be set to FALSE and the item that I created/just changed/updated to be set to true. Does that make sense?
Stacia
See other answer. I think that's more up your alley. I'm leaving this for posterity, though.
wesgarrison
A: 

Okay, I'm adding a separate answer because I think I get what you're saying.

You have a list of Items and you want one of them featured. Someone can pick one of those to be featured, but if not, there's a fallback item that gets featured by default.

If you have Store has_many :items, you could put an attribute for "featured_item_id" on the store and store the featured_id. If you need to set the default, add "default_item_id" and put the default one in there.

Add relationships for them on the Store:

belongs_to :featured_item, :class => Item, :foreign_key => 'featured_item_id'
belongs_to :default_item,  :class => Item, :foreign_key => 'default_item_id'

Then, make a method that test and returns the correct one:

def featured
  if self.featured_item_id
    self.featured_item
  else
    self.default_item
  end
end

If you set an item as the featured item, you get that one. If not, you get the default. You'd probably also want:

validates_presence_of :default_item_id

... to ensure there's always a default so you don't get a nil item problem.

wesgarrison
I guess this would work but it seems like overkill. I'm only dealing with booleans. It's either featured, or not. I just want a list of 00000 and then one 1 that is featured. Just zero everything out, and then place a true or one where I Updated this one.
Stacia
+1  A: 

In a transaction just set all to false and then update the record you want to true (featured). This is similar to how sometimes its easier to delete records then find them and update them. That is, its easier to just do something like:

tranasction do SomeModel.delete_all items.each { |i| SomeModel.create(i) } end

Which can be easier then looping over SomeModel doing a find() and then updating certain properties.

In your case you just want to mark ALL records a NOT-featured and then just update the one you want as featured, rather than toggling records by hand.

Try something like this:

after_create :set_as_featured

def set_as_featured
 self.class.transaction do
   self.connection.execute("UPDATE foo SET featured=0")
   self.update_attribute(:featured, true)
 end
end
Cody Caughlan
That's what I was trying to do!!! BUt it didn't work!! See my code. I need to check if it has changed after or before save or whatever and then just CLEAR everything and set the curernt item to true. I don't know why it doesn't work
Stacia
@stacia - see my updated entry. Basically just use a callback that clears out all others and then sets the newly created record as featured.
Cody Caughlan
why doesn't this work??? if @app.is_on_front_page_changed? == true App.is_on_front_page.map{|m| m.is_on_front_page = false} end
Stacia
Thank you SO much. I got it working with this:def set_as_featured self.class.transaction do App.update_all(:is_on_front_page => false) self.update_attribute(:is_on_front_page, true) endendHowever I still get an infinite loop on after update...what do I do?
Stacia
You are getting an infinite loop because each after_update callback is feeding into itself. Inside your after_update method you update the model which triggers another callback. You have two choices and it really depends on your product: 1) use after_create instead of after_update (with its obvious limitations) or 2) Hit the DB directly vs using update_attribute. Likeself.connection.execute("UPDATE foo SET featured=0")self.connection.execute("UPDATE foo SET featured=1 WHERE id = #{self.id)")This wont trigger any ActiveRecord callbacks because it side-steps AR.
Cody Caughlan
http://ariejan.net/2009/06/07/activerecord-skipping-callbacks-like-after_save-or-after_update/
Cody Caughlan
Good idea on using transactions. Way simpler than what I was thinking.
wesgarrison