Rails - why does one work and not the other? - ruby-on-rails

I have a seeds.rb file and for some reason, this doesn't work:
#doesn't work
u=User.new
u['email']=h['email']
u['password']=h['password']
puts u['email']
puts u['password']
if u.save
puts "that saved"
else
puts "that did not save"
end
but this does:
#does work
User.create({:email => h['email'], :password => h['password']})
Is there any reason one works and one doesn't? From rails console, the first does work? Would there be any differences in validations? I run 'rake db:seed' so would think validations would be in effect in both.
thx
edit #1
sorry for lack of info. each one errors with "password can't be blank" but it then echoes out the password. Hmm...

Find out what's happening by looking at the validation errors:
else
puts "that did not save because " + u.errors.join(' ')
end
Edit: Now that you've added that validation errors contain 'password cant be blank' and it still fails to save, it becomes clear that User.create() does a number of things which User.save() omits.
What's missing is the generation of your password hash - hence the blank password error.

Try it this way:
u = User.new
u.email = h['email']
u.password = h['password']
puts u.inspect
if u.save!
puts "that saved"
else
puts "that did not save"
end
I believe the approach of accessing the attribute on the u object as u['password'] is steering you wrong.
Also, doing a u.inspect prints the entire state of the model and that should provide a cleaner way of seeing what's happening. Finally, adding a bang ! at the end of the save will cause it to 'fail fast' and give you the error immediately.

Related

Why does delete not work on full_messages function?

I have the following code:
def register_learner
#event = Event.find(params[:event_id])
#registation = EventRegistration.new first_name: params[:first_name], last_name: params[:last_name], email: params[:email], event_id: params[:event_id]
if !#registation.valid?
#registation.errors.full_messages.delete("Event has already been taken"
flash[:notice] = #registation.errors.full_messages.to_sentence
redirect_to(event_path(#event))
else
#registation.save
end
end
Note the line #registation.errors.full_messages.delete("Event has already been taken") where I am trying to delete this particular message from the full_messages array, however it does not work. The next line is the flash message, and the message "Event has already been taken" is still being displayed.
Here is a sanity check via the console...
2.1.5 :001 > errors = ["Event has already been taken", "Last name can't be blank"]
=> ["Event has already been taken", "Last name can't be blank"]
2.1.5 :002 > errors.delete "Event has already been taken"
=> "Event has already been taken"
2.1.5 :003 > errors
=> ["Last name can't be blank"]
What am I missing?
This is because full_messages is a method, which gerneates a new array every time you call it. To do what you want:
errors = #registation.errors.full_messages
errors.delete("Event has already been taken")
flash[:notice] = errors.to_sentence
That answers the question, now there is a matter of - why do you need to do this? There might be a better way.
In general relying on strings is usually a bad idea, imagine that in a half a year you will need to change an error message for this validation. Can you be 100% sure you will remember to change it here as well? If not, you have a bug.
Well, because Rails re-creates #full_messages using errors every time you call the method:
def full_messages(options = {})
#errors.values.inject([]) do |full_messages, errors|
full_messages + errors.map { |error| error.full_message }
end
end
Source
You can use #select to skip that message:
#registation.errors.full_messages.select{|x| x != "Event has already been taken"}.to_sentence
Also, you can use #delete on #errors.messages (not full_messages), because they are exposed attribute of the underlying object (not a copy, as with full_messages):
#registration.errors.messages[:event].delete('has already been taken')

Flash[:error] add more than one at the same time

I use 'nokogiri' among others to check the schema from some uploaded xml's. And i will print out all errors which occurs:
xsd.validate(doc).each do |error|
flash[:error] = error.message
end
If I do so, I see only the last added error, if more than one exists.
I find also find a similar question, about this problem rails-easy-way-to-add-more-than-one-flashnotice-at-a-time but the accepted solution dosen't work for me.
Thanks
change the method to
flash[:error] = xsd.validate(doc).map(&:message).to_sentence
UPDATE
Using br tags to separate each error
flash[:error] = xsd.validate(doc).map(&:message).join('<br>').html_safe
I find also find a similar question, about this problem
rails-easy-way-to-add-more-than-one-flashnotice-at-a-time but the
accepted solution dosen't work for me.
In what way doesn't it work for you?
You can add your own flash types like flash[:errors] and write your own helper methods for convenience.
def my_flash(type, message)
flash[type] ||= []
flash[type] += Array.wrap(message)
end
Then you can use an array or a string as the message, making it easy to pass multiple in, like so.
my_flash :errors, "name cannot be blank"
my_flash :errors, ["age must be greater than 17", "phone number is invalid"]
p flash[:errors]
#=> ["name cannot be blank", "age must be greater than 17", "phone number is invalid"]

How to use shared_examples_for in rsepc-mock?I have try many times but failed.who can help ,please? Wait online

I am a freshman on Rspec
it "should not ask the database" do
#sqlite_database.should_not_receive(:findISBN)
#result = #cache.findISBN('1234')
#result.should eql(#book)
end
it "should not ask the database" do
#sqlite_database.should_not_receive(:authorSearch)
#result = #cache.authorSearch('author')
#result.should eql(#book)
end
Here are two different part 1,:findISBN and :authorSearch 2 findISBN('1234') and authorSearch('author')
I try to use let but it doesn't work ,who can help ?
#sqlite_database = double()
#cache = SQLiteDataBaseWithCache.new(#sqlite_database)
That's ture ,I'm coming from java background .You coding showed some warning: syntax error, unexpected ':', expecting keyword_end (SyntaxError).I have no ideal about it
without more details on the variables like #sqlite_database, #cache etc its hard to tell whats happening, but following should work
probably a stack trace would help
following is the idea of shared examples :)
shared_examples "it should not ask database" do |symbol, params|
it "should not ask the database" do
#sqlite_database.should_not_receive(symbol)
#result = #cache.send symbol, params
#result.should eql(#book)
end
end
it_behaves_like "it should not ask the database", :findISBN, '1234'
it_behaves_like "it should not ask the database", :authorSearch, 'author'
and on a side note, your method signatures are not ruby kind of... in ruby we normally dont use camel case
so in ruby/rails its should be author_search, probably you are coming from java background :)

Setting path in Cucumber

I am setting up a cucumber scenario for setting up a valid user where my last step is:
"Then I should be taken to the show user page"
which I define as:
Then /I should be taken to the show user page/ do
#user = User.last
if current_path.respond_to? :should
current_path.should == path_to(user_path(#user))
else
assert_equal path_to(user_path(#user)), current_path
end
visit(user_path(#user))
end
After getting an error "Can't find mapping from "/users/49" to a path." I attempted to define the path as:
when /^users\/(.+)$/ do |user|
user_path(user.to_i)
end
But this yields the error:
syntax error, unexpected keyword_do, expecting keyword_then or ',' or ';' or '\n'
when /^landlords/(.+)$/ do |landlord|
I am relatively new to rails and web development and completely new to cucumber and TDD. Also new to regex. Any help would be appreciated!
Thanks,
John
It looks like user_path(#user) is giving you the path you need, so wrapping that in path_to is causing the error as it tries to do the same thing.
I think getting rid of the path_to call might help:
current_path.should == user_path(#user)
The reason for the "unexpected keyword_do" error is because you have when instead of When, i.e. Ruby is interpreting it as a case-style statement. But as Jon M points, you don't need that anyway.

Specing a manual call to valid?

Hey all, I am completely lost on this one.
I found a code snippet online to help validate fields via ajax as the user types into them. So I'm trying to write a spec against part of it and I just can't get it to pass.
Here's the code
def validate
field = params[:field]
user = User.new(field => params[:value])
output = ""
user.valid?
if user.errors[field] != nil
if user.errors[field].class == String
output = "#{field.titleize} #{user.errors[field]}"
else
output = "#{field.titleize} #{user.errors[field].to_sentence}"
end
end
render :text => output
end
and here is my test so far
describe "POST validate" do
it "retrieves the user based on the past in username" do
mock_errors ||= mock("errors")
mock_errors.stub!(:[]).and_return(nil)
User.should_receive(:new).with({'username'=>"UserName"}).and_return(mock_user)
mock_user.should_receive(:valid?).and_return(true)
mock_errors.should_receive(:[]).with("username").and_return(nil)
put :validate, :field=>'username', :value=>'UserName'
response.should == ""
end
end
I get this error -
1) Spec::Mocks::MockExpectationError
in 'UsersController POST validate
retrieves the user based on the past
in username' Mock 'errors' received
unexpected message :[] with
("username")
I can't seem to figure out how in the world to mock the call to user.errors[field]. Ideally this spec tests the happy path, no errors. I'll then write another for a validation failure.
I'm not seeing mock_user. Here's a shot at it:
describe "POST validate" do
it "retrieves the user based on the past in username" do
mock_errors = mock("errors")
mock_user = mock("user")
mock_user.stub!(:errors).and_return([mock_errors])
mock_errors.stub!(:[]).and_return(nil)
User.should_receive(:new).with({'username'=>"UserName"}).and_return(mock_user)
mock_user.should_receive(:valid?).and_return(true)
mock_errors.should_receive(:[]).with("username").and_return(ActiveRecord::Errors.new({}))
put :validate, :field=>'username', :value=>'UserName'
response.should == ""
end
end
The key is that you need your User mock to respond to the errors method by returning either an empty hash or a hash of fieldname/errors. An alternative to this is to use one of the fixture replacement tools. I'm using machinist right now, which might reduce this whole thing to:
describe "POST validate" do
it "retrieves the user based on the past in username" do
#user = User.make{'username'=>"UserName"}
#user.should_receive(:valid?).and_return(true)
#user.errors.should_receive(:[]).with("username").and_return(ActiveRecord::Errors.new({}))
put :validate, :field=>'username', :value=>'UserName'
response.should == ""
end
end

Resources