2

I have a Rails model named Container with a column named products. It is a string array as supported by Postgres and the 'postgres_ext' gem.

The relevant portion of the GEMFILE is:

gem 'rails', '3.2.9'
gem 'pg'
gem 'postgres_ext'
gem 'activerecord-postgres-hstore', git: 'git://github.com/engageis/activerecord-postgres-hstore.git'

The relevant portion of the migration is:

 t.string :products, array: true

I am writing a public method in my Container model which adds products to this array. The method looks like this:

 attr_accessible :products

 def add_to_products(product)

  if products.blank? || products.size == 0 ## product array is either uninstantiated or blank
    products = [product.id]
  else  

    unless products.include? product.id
      products << product.id
    end
  end
end

These are the results in irb/console:

pry(main)> c = Container.first
=> #<Container id: "2765cc19-98f8-4e42-a1be-538788424ec7", name:....
pry(main)> p = Product.first
=> #<Product id: "319a25ae-87fe-4769-a9de-1a8e0db9e84f", name: ....
pry(main)> c.add_to_products(product)
pry(main)> c.products
=> nil
pry(main)> c.products= [] << "319a25ae-87fe-4769-a9de-1a8e0db9e84f"
pry(main)> c.products
=> ["319a25ae-87fe-4769-a9de-1a8e0db9e84f"]

I am scratching my head to figure out what's wrong in the add_to_products method. Can someone throw some light on this weird situation? Why is the value not being set when I pass it through this method?

Dan McClain
  • 11,780
  • 9
  • 47
  • 67
paddle42380
  • 6,921
  • 7
  • 32
  • 40
  • By "postgre", do you mean "PostgreSQL"? – the Tin Man Jan 01 '13 at 15:58
  • isnt it evident? Dont understand the value of the comment. Edited title, none the less. – paddle42380 Jan 01 '13 at 16:29
  • No, it's not evident. The value of the comment was for clarification. "PostgreSQL" is the name of the DBM and it's sometimes abbreviated to "Postgres" because people don't like saying the entire name. (The team acknowledges it's unwieldy.) "Postgre" could be that or something else because StackOverflow receives questions from users around the world and they don't always spell well or know the right name. – the Tin Man Jan 01 '13 at 17:36

2 Answers2

5

This issue is actually arising out of the use of <<. Rails does not track in place modifications of attributes (see this issue). I outline in the usage notes that you want to avoid the << operator when using arrays, as rails will not see this change, it will actually cause issues with default values.

This can also be seen by checking the products_changed? state. It will be false when you use <<.

Dan McClain
  • 11,780
  • 9
  • 47
  • 67
2

This has got to do with the fact that in Ruby assignments create local variables unless you explicitly state the receiver, which is self in this case. So:

products = [product.id]

...will create a local variable named products. Whereas:

self.products = [product.id]

...is what you are really looking for.

Casper
  • 33,403
  • 4
  • 84
  • 79
  • while that makes sense, self.products did not really solve the problem. – paddle42380 Jan 02 '13 at 14:19
  • @papdel Did you restart the server? Have you added debugging code to your `add_to_products` method? If not then you should, because it looks like something in there is not happening like you expect. So debug it. Just staring at the code doesn't help. Isolate the problem. `logger.debug` is your friend. Also you might want to read: http://guides.rubyonrails.org/debugging_rails_applications.html – Casper Jan 02 '13 at 16:50
  • This is due to the way attribute changes are tracked in rails. See my answer for more details – Dan McClain Feb 04 '13 at 15:24
  • Thanks Dan, I bypassed it through a different route, but this was exactly what I was looking for. – paddle42380 Feb 04 '13 at 19:08