I have a model WordpressBlogger that has these attributes: (note the specific order of these columns: screen_name was added AFTER the table was generated)
id, rss_feed_id, blog_url, year_founded, created_at, updated_at, screen_name, latest_entry
When I do blogger = WordpressBlogger.new(:screen_name => "test") in my console, it doesn't seem to return a model with the screen_name set.
Instead I get this:
=> #<WordpressBlogger id: nil, rss_feed_id: nil, blog_url: nil, year_founded: nil, created_at: nil, updated_at: nil, screen_name: nil, latest_entry: nil>
Yet, when I do blogger = WordpressBlogger.new(:year_founded => 2011) in my console, it returns a model with this attribute set.
=> #<WordpressBlogger id: nil, rss_feed_id: nil, blog_url: nil, year_founded: 2011, created_at: nil, updated_at: nil, screen_name: nil, latest_entry: nil>
Is this a weird Rails bug where the attribute doesn't get set if the column in the database is AFTER Rails-created attributes such as created_at and updated_at?
UPDATE: I forgot to add it to attr_accessible.
Most likely, there is a reference to attr_accessible in the WordpressBlogger class. You probably need to add year_founded to the list of attr_accessible elements that are allowed bulk assignment.
It's actually not creating the Model. Notice there's no id set. To create it you must either
blogger = WordpressBlogger.new(:screen_name => "test")
blogger.save
or
blogger = WordpressBlogger.create(:screen_name => "test")
Is this in your model?
attr_accessor :screen_name, :year_founded
Or you may have to exit and run rails c again
You most probably have the console running when you ran your migrations. A quick reload! or restart of the console should make all columns visible.
Related
I have a Recoverable module for my Customer model. Customer model using has_secure method for authentication. Here is the Customer model:
class Customer < ActiveRecord::Base
include Recoverable
##
# Validations
validates :email, format: { with: REGEX_EMAIL }, allow_nil: false, allow_blank: false
validates_uniqueness_of :email
validates_presence_of :email
has_secure_password
validates :password, length: { minimum: 6 }, if: :password_digest_changed?
validates :password_confirmation, presence: true, if: :password_digest_changed?
end
And here is the Recoverable module:
# encoding: utf-8
module Recoverable
extend ActiveSupport::Concern
def reset_password!(new_password, new_password_confirmation)
self.password = new_password
self.password_confirmation = new_password_confirmation
if valid?
self.reset_password_token = nil
self.reset_password_sent_at = nil
end
save
end
end
My problem is after reset_password called reset_password_token, reset_password_sent_at are not null. It's not set to null. Update query is not set below columns. Why? Am I miss something? If you need more info let me know.
My environments: I'm using Rails 4 app.
UPDATE 1
When I puts self.inspect I get following outputs:
#<Customer id: 79, email: "milk#yahoo.com", password_digest: "$2a$10$U2knjpm5LF1V/sgXag0DcOpgZWHSpLw8nfCy4U8D57s6...", created_at: "2013-05-11 11:55:34", updated_at: "2013-05-16 10:04:45", reset_password_sent_at: nil, reset_password_token: nil>
UPDATE 2:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"PbUhgSPvQZWXflT5fA1WhqhHJX3c7NMapg6eeDQvpBI=", "token"=>"fiMXi2_4cYCHsFMop9TJBL2Qeqc41xWhHA", "q"=>{"password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}
Unpermitted parameters: utf8, _method, authenticity_token, q
Customer Load (0.4ms) SELECT "customers".* FROM "customers" WHERE "customers"."reset_password_token" IS NULL LIMIT 1
Unpermitted parameters: password_confirmation
Unpermitted parameters: password
Customer Exists (0.3ms) SELECT 1 AS one FROM "customers" WHERE ("customers"."email" = 'milk#yahoo.com' AND "customers"."id" != 79) LIMIT 1
----------------------------BEFORE:
#<ActiveModel::Errors:0xb593c280 #base=#<Customer id: 79, email: "milk#yahoo.com", password_digest: "$2a$10$/xYeks8yyaCMOFORFLMb1.xR7fxfskW6kHR4S2df/LTK...", store_id: 124, created_at: "2013-05-11 11:55:34", updated_at: "2013-05-16 11:56:52", reset_password_sent_at: nil, reset_password_token: nil>, #messages={}>
(0.1ms) BEGIN
CACHE (0.0ms) SELECT 1 AS one FROM "customers" WHERE ("customers"."email" = 'milk#yahoo.com' AND "customers"."id" != 79) LIMIT 1
SQL (0.3ms) UPDATE "customers" SET "password_digest" = $1, "updated_at" = $2 WHERE "customers"."id" = 79 [["password_digest", "$2a$10$/xYeks8yyaCMOFORFLMb1.xR7fxfskW6kHR4S2df/LTKUI001xu0O"], ["updated_at", Thu, 16 May 2013 19:58:25 ULAT +08:00]]
(16.5ms) COMMIT
---------------------------- SAVE:
true
----------------------------AFTER:
#<ActiveModel::Errors:0xb593c280 #base=#<Customer id: 79, email: "milk#yahoo.com", password_digest: "$2a$10$/xYeks8yyaCMOFORFLMb1.xR7fxfskW6kHR4S2df/LTK...", store_id: 124, created_at: "2013-05-11 11:55:34", updated_at: "2013-05-16 11:58:25", reset_password_sent_at: nil, reset_password_token: nil>, #messages={}>
Ok so finally if your model is not valid after clearing variables you can do that:
save(validate: false)
It will skip validation and will allow you to save invalid model
Could you check if your model is really valid ?
I mean something like
if valid?
puts "valid"
self.reset_password_token = nil
self.reset_password_sent_at = nil
else
puts self.errors.inspect
end
Maybe you have some forgotten validation and you are not going to that block ?
I have a bunch of users in my database with these attributes, however, I only want the email address for each user
#<User id: 1, email: "email#yahoo.com", encrypted_password: "", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, created_at: "2012-09-03 09:14:01", updated_at: "2012-09-03 09:14:01", name: nil, confirmation_token: nil, confirmed_at: nil, confirmation_sent_at: nil, unconfirmed_email: nil, opt_in: nil, invitation_token: nil, invitation_sent_at: nil, invitation_accepted_at: nil, invitation_limit: nil, invited_by_id: nil, invited_by_type: nil>,
In the console, I did
u = User.all
which printed all the users and their attributes.
Now, to get the email address for each i tried
u.each do |f|
f.email
end
but it just printed the whole list of users again, with all their attributes.
Can anyone show me how to print a list of email addresses for all the users, leaving out the other attributes.
Your console will print at the end the result of what you typed.
So if you write u.each { anything }, the console will print the result of the each loop. To print stuff explicitly, you need to use output function (puts, p, pp, print etc)
users = User.all
puts users.map(&:email).join("\n")
When I clone a simple object in ruby-1.9.2-p290, everything looks OK
class Klass
attr_accessor :str
end
s1 = Klass.new #=> #<Klass:0x401b3a38>
s1.str = "Hello" #=> "Hello"
s2 = s1.clone #=> #<Klass:0x401b3998 #str="Hello">
s2.str = "Hello world" #=> "Hello world"
s2 #=> #<Klass:0x00000100977c40 #str="Hello world">
s1 #=> #<Klass:0x00000100993fa8 #str="Hello">
But when I clone an ActiveRecord object then something strange happens:
I am using the rails 3.1.8.
Loading development environment (Rails 3.1.8).
When I start the 'rails console'.
ruby-1.9.2-p290 :001 > chair = Chair.new(:code => 'code', :description => 'The Description')
#=> #<Chair id: nil, code: "code", description: "The Description", user_id: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p290 :002 > chair_clone = chair.clone
#=> #<Chair id: nil, code: "code", description: "The Description", user_id: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p290 :003 > chair_clone.description = "Update description"
#=> "Update description"
ruby-1.9.2-p290 :004 > chair_clone
#=> #<Chair id: nil, code: "code", description: "Update description", user_id: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p290 :005 > chair
#=> #<Chair id: nil, code: "code", description: "Update description", user_id: nil, created_at: nil, updated_at: nil>
Isn't it strange that the description attribute of the original object 'chair' is also updated.
I found the following warning in the http://apidock.com/ruby/Object/clone doc
Change in clone for ActiveRecord objects in ruby-1.9.3
I noticed that cloning an active record object in ruby-1.9.3 and then changing an attribute on the original object will actually change the cloned object as well. This was not the case in ruby-1.9.2.
Is there already a solution available for this issue ?
Thanks in advance for any feedback.
Joost
Instead of using clone use dup like:
...
chair_clone = chair.dup
...
u = User.last
u.duplicable? # => true
u2 = u.dup
u2.email = 'wwwww'
u.email # => 'megacoder#rambler.ru'
u2.email # => 'wwwww'
I have the following piece of code that checks if the returned video object is the same as the one I put in.
The video object is a Panda Stream object, and referring to their homepage, the Panda::Video.find should return exactly the object I put in with the Panda::Video.create method.
# screencast_spec.rb
before(:each) do
#screencast = Factory.build(:screencast)
#video = Panda::Video.create(:source_url => "http://panda-test-harness-videos.s3.amazonaws.com/panda.mp4")
end
it "should fetch the video with the #video method" do
#screencast.video = #video
#screencast.video.should == #video
end
# screencast.rb
# Returns the panda::video object
def video
Panda::Video.find(self.video_id)
end
# Sets the +video_id+ reference
def video= video_object_or_id
video_object_or_id = video_object_or_id.id unless video_object_or_id.is_a?(Integer)
self.video_id = video_object_or_id
end
The error that RSpec returns is this:
1) Screencast#video should fetch the video with the #video method
Failure/Error: #screencast.video.should == #video
expected: #<Panda::Video id: "dac9bf057c9b667f57096054a64625a1", created_at: "2012/01/29 18:04:07 +0000", updated_at: "2012/01/29 18:04:07 +0000", original_filename: "panda.mp4", source_url: "http://panda-test-harness-videos.s3.amazonaws.com/panda.mp4", duration: nil, height: nil, width: nil, extname: ".mp4", file_size: nil, video_bitrate: nil, audio_bitrate: nil, audio_codec: nil, video_codec: nil, fps: nil, audio_channels: nil, audio_sample_rate: nil, status: "processing", path: "dac9bf057c9b667f57096054a64625a1", cloud_id: "55372b1612963b045f27bb093fed0abb">
got: #<Panda::Video id: "dac9bf057c9b667f57096054a64625a1", created_at: "2012/01/29 18:04:07 +0000", updated_at: "2012/01/29 18:04:07 +0000", original_filename: "panda.mp4", source_url: "http://panda-test-harness-videos.s3.amazonaws.com/panda.mp4", duration: nil, height: nil, width: nil, extname: ".mp4", file_size: nil, video_bitrate: nil, audio_bitrate: nil, audio_codec: nil, video_codec: nil, fps: nil, audio_channels: nil, audio_sample_rate: nil, status: "processing", path: "dac9bf057c9b667f57096054a64625a1", cloud_id: "55372b1612963b045f27bb093fed0abb"> (using ==)
Diff:
# ./spec/models/screencast_spec.rb:26:in `block (3 levels) in <top (required)>'
Note the diff saying that there is no difference between the two objects, so why is the comparison failing?
If any of you have any pointers, or have tried testing the panda stream before I would appreciate all the guidance I can get
ruby provides 3 methods for testing for equality == eql? equals?. when using == you are comparing by object identity. ruby uses the object-id for this. the important thing is that classes can redefine == like it's done for string-comparison.
if you wan't to learn more on this topic i can advise you this blog post: http://www.skorks.com/2009/09/ruby-equality-and-object-comparison/
With the following models:
class Location < ActiveRecord::Base
has_many :group_locations
has_many :groups, :through => :group_locations
accepts_nested_attributes_for :group_locations
end
class GroupLocation < ActiveRecord::Base
belongs_to :group
belongs_to :location
end
class Group < ActiveRecord::Base
has_many :group_locations
has_many :locations, :through => :group_locations
end
the following commands in rails console does not update the associated records:
>> l = Location.find(1)
=> #<Location id: 1, phone: "(949) 788-9999", ... created_at: "2011-06-02 00:58:07",
updated_at: "2011-06-07 23:57:32">
\>\> l.group_locations
=> [#<GroupLocation group_id: 4, location_id: 1, created_at: "2011-06-02 00:58:07",
updated_at: "2011-06-02 00:58:07">, #<GroupLocation group_id: **37**, location_id: 1,
created_at: "2011-06-02 00:58:07", updated_at: "2011-06-02 00:58:07">]
>> l.update_attributes(:phone => "(949) 788-9998", :group_locations_attributes =>
[{:group_id => 4, :location_id => 1}, {:group_id => **38**, :location_id => 1}])
=> true
>> l
=> #<Location id: 1, phone: "(949) 788-9998", ... created_at: "2011-06-02 00:58:07",
updated_at: "2011-06-08 02:05:00">
>> l.group_locations
=> [#<GroupLocation group_id: 4, location_id: 1, created_at: "2011-06-02 00:58:07",
updated_at: "2011-06-02 00:58:07">, #<GroupLocation group_id: **37**, location_id: 1,
created_at: "2011-06-02 00:58:07", updated_at: "2011-06-02 00:58:07">]
Note that the update_attributes call attempts to change the second GroupLocation to have group_id = 38, but the change is not made (even though the phone number did change). After looking at the code generated when this was implemented in the controller and view, changing the array to a hash (which is what is created in that case) has no different results (and the form/controller) have the same effect of not updating the associated records even though the main record is updated.
Any idea what I need to do to get the nested attributes to update?
From the logs you've displayed, it doesn't appear that your GroupLocation model has an :id primary key on it. While the join table for a HABTM has just the foreign keys (group_id, location_id) on it, the model used for a has_many :through association does need a primary key as well, :id by default. Otherwise, there is no way to determine which of the child objects to update in the case of an update.
Think of it this way - you are creating your association through another discrete model that should be able to stand entirely on its own.
The convention for nested attributes is if the hash passed to the nested_attributes includes an :id, then it is considered an update, if it doesn't then it's considered a create. In your case, you're not passing in an :id, so you get new GroupLocation records where you just wanted to update existing.
I believe, also, that once you have this in place correctly, you will be able to get rid of the attr_accessible, I don't think that should be necessary.
For good info on the nested attributes functionality that covers most of this, check out this page.
The actual answer is that the nested attributes must be accessible via attr_accessible. "accepts_nested_attributes" will only do what I want if it is accompanied by "attr_accessible :group_locations".