Rails error with rspec: "expected valid? to return true, got false" - ruby-on-rails-3

I'm new to rails, and after completing Michael Hartl's Ruby on Rails Tutorial, I am trying to expand some of the functionality. I started by changing the microposts so that they collect more data.
When running a rspec test, I can't figure out why I am getting this error:
$ bundle exec rspec spec/models/micropost_spec.rb
...............F.....
Failures:
1) Micropost
Failure/Error: it { should be_valid }
expected valid? to return true, got false
# ./spec/models/micropost_spec.rb:46:in `block (2 levels) in <top (required)>'
Finished in 1.57 seconds
21 examples, 1 failure
Failed examples:
rspec ./spec/models/micropost_spec.rb:46 # Micropost
micropost_spec.rb
require 'spec_helper'
describe Micropost do
let(:user) { FactoryGirl.create(:user) }
before { #micropost = user.microposts.build(content: "Lorem ipsum",
title: "This is a test title",
privacy: "1",
groups: "This is a test Group",
loc1T: "21 Bond St. Toronto, Ontario",
loc1Lat: "43.654653",
loc1Lon: "-79.377627",
loc2T: "21 Bond St. Toronto, Ontario",
loc2Lat: "43.654653",
loc2Lon: "-79.377627",
startTime: "Jan 1, 2000 12:01:01",
endTime: "Jan 2, 2000 12:01:01",
imageURL: "http://i.cdn.turner.com/cnn/.e/img/3.0/global/header/hdr-main.gif")
puts #micropost.errors.messages
}
#before { #micropost = user.microposts.build(title: "This is a test title") }
#before { #micropost = user.microposts.build(privacy: "1") }
#before { #micropost = user.microposts.build(groups: "This is a test Group") }
#before { #micropost = user.microposts.build(loc1T: "21 Bond St. Toronto, Ontario") }
#before { #micropost = user.microposts.build(loc1Lat: "43.654653") }
#before { #micropost = user.microposts.build(loc1Lon: "-79.377627") }
#before { #micropost = user.microposts.build(loc2T: "21 Bond St. Toronto, Ontario") }
#before { #micropost = user.microposts.build(loc2Lat: "43.654653") }
#before { #micropost = user.microposts.build(loc2Lon: "-79.377627") }
#before { #micropost = user.microposts.build(startTime: "Jan 1, 2000 12:01:01") }
#before { #micropost = user.microposts.build(endTime: "Jan 2, 2000 12:01:01") }
#before { #micropost = user.microposts.build(imageURL: "http://i.cdn.turner.com/cnn/.e/img/3.0/global/header/hdr-main.gif") }
subject { #micropost }
it { should respond_to(:content) }
it { should respond_to(:user_id) }
it { should respond_to(:user) }
it { should respond_to(:title) }
it { should respond_to(:privacy) }
it { should respond_to(:groups) }
it { should respond_to(:loc1T) }
it { should respond_to(:loc1Lat) }
it { should respond_to(:loc1Lon) }
it { should respond_to(:loc2T) }
it { should respond_to(:loc2Lat) }
it { should respond_to(:loc2Lon) }
it { should respond_to(:startTime) }
it { should respond_to(:endTime) }
it { should respond_to(:imageURL) }
its(:user) { should == user }
it { should be_valid }
describe "accessible attributes" do
it "should not allow access to user_id" do
expect do
Micropost.new(user_id: user.id)
end.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
end
describe "when user_id is not present" do
before { #micropost.user_id = nil }
it { should_not be_valid }
end
describe "with blank content" do
before { #micropost.content = " " }
it { should_not be_valid }
end
describe "with content that is too long" do
before { #micropost.content = "a" * 141 }
it { should_not be_valid }
end
end
micropost.rb
class Micropost < ActiveRecord::Base
attr_accessible :content, :title,:privacy,:groups,:loc1T,:loc1Lat,:loc1Lon,:loc2T,:loc2Lat,:loc2Lon,:startTime,:endTime,:imageURL
belongs_to :user
validates :user_id, presence: true
validates :title, presence: true
validates :privacy, presence: true
validates :groups, presence: true
validates :loc1T, presence: true
validates :loc1Lat, presence: true
validates :loc1Lon, presence: true
validates :loc2T, presence: true
validates :loc2Lat, presence: true
validates :loc2Lon, presence: true
validates :startTime, presence: true
validates :endTime, presence: true
validates :imageURL, presence: true
validates :content, presence: true, length: { maximum: 140 }
default_scope order: 'microposts.created_at DESC'
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
user_id: user.id)
end
end
factories.rb
FactoryGirl.define do
factory :user do
#name "Michael Hartl"
#email "michael#example.com"
sequence(:name) { |n| "Person #{n}" }
sequence(:email) { |n| "person_#{n}#example.com"}
password "foobar"
password_confirmation "foobar"
factory :admin do
admin true
end
end
factory :micropost do
content "Lorem ipsum"
title "This is a test title"
privacy "1"
groups "This is a test Group"
loc1T "21 Bond St. Toronto, Ontario"
loc1Lat "43.654653"
loc1Lon "-79.377627"
loc2T "21 Bond St. Toronto, Ontario"
loc2Lat "43.654653"
loc2Lon "-79.377627"
startTime "Jan 1, 2000 12:01:01"
endTime "Jan 2, 2000 12:01:01"
imageURL "http://i.cdn.turner.com/cnn/.e/img/3.0/global/header/hdr-main.gif"
user
end
end
I'm not sure if there is anything else you need me to post.

It is not valid because your model object is not passing your validations. In your before block, after you create #micropost, add this line to see which validations are failing
puts #micropost.errors.messages
There will be a hash of validations (fields and error messages) that have failed. Fix those and then your object will be valid. Some earlier commenters had suggestions on where to start to help fix the problem.

This got rid of the errors.
micropost_spec.rb
describe Micropost do
let(:user) { FactoryGirl.create(:user) }
before { #micropost = user.microposts.build(content: "Lorem ipsum",
title: "This is a test title",
privacy: "1",
groups: "This is a test Group",
loc1T: "21 Bond St. Toronto, Ontario",
loc1Lat: "43.654653",
loc1Lon: "-79.377627",
loc2T: "21 Bond St. Toronto, Ontario",
loc2Lat: "43.654653",
loc2Lon: "-79.377627",
startTime: "Jan 1, 2000 12:01:01",
endTime: "Jan 2, 2000 12:01:01",
imageURL: "http://i.cdn.turner.com/cnn/.e/img/3.0/global/header/hdr-main.gif")
puts #micropost.errors.messages
}

Related

How to get has_and_belongs_to_many relation objects together

I have HABTM associated tables like below.
class User < ActiveRecord::Base
has_and_belongs_to_many :groups
end
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
end
How do I get User(s) with associated Groups together(nested), like below
{
id: 8,
name: "John",
groups: [
{
id: 17,
name: "Alpha Group",
user_id: 8
},
{
id: 18,
name: "Bravo Group",
user_id: 8
}
],
},
Is there a nice rails way of doing this?
I can manually create object like above but it would be nice to have a simpler query.
def index
#group = Group.find(params[:id])
respond_to do |format|
format.html
format.json { #group.users.map do |user|
user.to_json(:include => :groups)
end
}
end
end
It'll return an array like:
[
{
"id":1,
"email":"admin#example.com",
"groups":[
{
"id":1,
"name":"Yo"
},
{
"id":2,
"name":"Bro"
},
]
},
{
"id":2,
"email":"valar#example.com",
"groups":[
{
"id":1,
"name":"Arya"
},
{
"id":2,
"name":"Stark"
},
]
}
]

How to rename/alias rabl nodes?

I am developing a Rails 3.2.14 app and I am using Rabl for outputting API data.
I want to change the names of the root node (notifications) and object node (notification) for the output below. How can I do that?
{
"total": 1,
"notifications": [
{
"notification": {
"id": 2,
"subject": "Testing",
"body": "Testing",
"created_at": "16 Jan 2014 14:22",
"conversation": 2,
"sender_id": 5,
"sender_name": "Mike Swanson"
}
}
]
}
The show.rabl file:
object false
node(:total) { #output.count }
child(#output) { extends 'api/v1/shared/conversation_rich' }
The conversation_rich file:
attributes :id, :subject, :body
node(:created_at) { |message| message.created_at.strftime("%e %b %Y %H:%M") }
node(:conversation) { |message| message.conversation.id }
node(:sender_id) { |message| message.sender.id }
node(:sender_name) { |message| message.sender.fullname }
Update
Turns out it was extremely simple. Just add => :messages:
child(#output => :messages) { extends 'api/v1/shared/conversation_rich' }
Turns out it was extremely simple. Just add => :messages:
child(#output => :messages) { extends 'api/v1/shared/conversation_rich' }

Autosave issue in rails 3.2

In the given code,
class Supplier < ActiveRecord::Base
has_one :criteria, foreign_key: "crt_sup_id", :autosave => true
self.primary_key = 'sup_id'
end
class Criteria < ActiveRecord::Base
belongs_to :supplier, foreign_key: "crt_sup_id"
self.primary_key = 'crt_id'
self.table_name = 'criterias'
end
autosave is not working when I am submitting the form. Supplier records are created but not Criteria.
Form code
class SupplierForm < Netzke::Basepack::Form
def configure(c)
c.model = 'Supplier'
super
c.items = [
{field_label: "Name", name: :bname},
{field_label: "Detail", name: :detail},
{
layout: :hbox, border: false, defaults: {border: false}, items: [
{
flex: 1,
layout: :anchor,
defaults: {anchor: "-8"},
items: [
{field_label: "Value 1", name: :criteria__val_one, xtype: :checkbox, nested_attribute: true},
{field_label: "Value 2", name: :criteria__val_two, xtype: :checkbox, nested_attribute: true}
]
}
]
}
]
end
end
Controller code
def index
end
Solved with the help of Netzke author. Replace criteria__val_one with criteria_val_one and
criteria__val_two with criteria_val_two. Create virtual attributes in the model class. Now all the values entered in the form is accessible with these virtual attributes and can be saved. Credit goes to Max Gorin. Thanks for the great work (Netzke)

Michael Hartl Ruby on Rails tuttorial chapter9 Rspec Fail

Hi i'm a newbie to Rails. Ruby on Rails Tutorial Chapter 9, Rspec faild. rails ver. was 3.2.6. This source is github. I have 3 failing tests to complete the Chapter.
Terminal returns this:
$ bundle exec rspec spec/requests/user_pages_spec.rb
......F.FF.................
Failures:
1) User pages index pagination should list each user
Failure/Error: page.should have_selector('li', text: user.name)
expected css "li" with text "Person 41" to return something
# ./spec/requests/user_pages_spec.rb:44:in `block (5 levels) in <top (required)>'
# ./spec/requests/user_pages_spec.rb:43:in `each'
# ./spec/requests/user_pages_spec.rb:43:in `block (4 levels) in <top (required)>'
2) User pages delete links as an admin user
Failure/Error: it { should have_link('delete', href: user_path(User.first)) }
expected link "delete" to return something
# ./spec/requests/user_pages_spec.rb:61:in `block (4 levels) in <top (required)>'
3) User pages delete links as an admin user should be able to delete another user
Failure/Error: expect { click_link('delete') }.to change(User, :count).by(-1)
Capybara::ElementNotFound:
no link with title, id or text 'delete' found
# (eval):2:in `click_link'
# ./spec/requests/user_pages_spec.rb:63:in `block (5 levels) in <top (required)>'
# ./spec/requests/user_pages_spec.rb:63:in `block (4 levels) in <top (required)>'
Finished in 4.6 seconds
27 examples, 3 failures
Failed examples:
rspec ./spec/requests/user_pages_spec.rb:42 # User pages index pagination should list each user
rspec ./spec/requests/user_pages_spec.rb:61 # User pages delete links as an admin user
rspec ./spec/requests/user_pages_spec.rb:62 # User pages delete links as an admin user should be able to delete another user
Spec File is This:
$ cat spec/requests/user_pages_spec.rb
require 'spec_helper'
describe "User pages" do
subject { page }
describe "index" do
let(:user) { FactoryGirl.create(:user) }
before(:all) { 30.times { FactoryGirl.create(:user) } }
after(:all) { User.delete_all }
before do
sign_in user
visit users_path
end
it { should have_selector('title', text: 'All users') }
it { should have_selector('h1', text: 'All users') }
describe "pagination" do
it { should have_selector('div.pagination') }
it "should list each user" do
User.paginate(page: 1).each do |user|
page.should have_selector('li', text: user.name)
end
end
before do
sign_in FactoryGirl.create(:user)
FactoryGirl.create(:user, name: "Bob", email: "bob#example.com")
FactoryGirl.create(:user, name: "Ben", email: "ben#example.com")
visit users_path
end
it { should have_selector('title', text: 'All users') }
it { should have_selector('h1', text: 'All users') }
it "should list each user" do
User.all.each do |user|
page.should have_selector('li', text: user.name)
end
end
end
end
describe "delete links" do
it { should_not have_link('delete') }
describe "as an admin user" do
let(:admin) { FactoryGirl.create(:admin) }
before do
sign_in admin
visit users_path
end
it { should have_link('delete', href: user_path(User.first)) }
it "should be able to delete another user" do
expect { click_link('delete') }.to change(User, :count).by(-1)
end
it { should_not have_link('delete', href: user_path(admin)) }
end
end
describe "profile page" do
let(:user) { FactoryGirl.create(:user) }
before { visit user_path(user) }
it { should have_selector('h1', text: user.name) }
it { should have_selector('title', text: user.name) }
end
describe "signup page" do
before { visit signup_path }
it { should have_selector('h1', text: 'Sign up') }
it { should have_selector('title', text: full_title('Sign up')) }
end
describe "signup" do
before { visit signup_path }
let(:submit) { "Create my account" }
describe "with invalid information" do
it "should not create a user" do
expect { click_button submit }.not_to change(User, :count)
end
end
describe "with valid information" do
before do
fill_in "Name", with: "Example User"
fill_in "Email", with: "user#example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
describe "after saving the user" do
before { click_button submit }
it { should have_link('Sign out') }
end
end
end
describe "edit" do
let(:user) { FactoryGirl.create(:user) }
before do
sign_in user
visit edit_user_path(user)
end
describe "page" do
it { should have_selector('h1', text: "Update your profile") }
it { should have_selector('title', text: "Edit user") }
it { should have_link('change', href: 'http://gravatar.com/emails') }
end
describe "with invalid information" do
before { click_button "Save changes" }
it { should have_content('error') }
end
describe "with valid information" do
let(:new_name) { "New Name" }
let(:new_email) { "new#example.com" }
before do
fill_in "Name", with: new_name
fill_in "Email", with: new_email
fill_in "Password", with: user.password
fill_in "Confirm Password", with: user.password
click_button "Save changes"
end
it { should have_selector('title', text: new_name) }
it { should have_selector('div.alert.alert-success') }
it { should have_link('Sign out', href: signout_path) }
specify { user.reload.name.should == new_name }
specify { user.reload.email.should == new_email }
end
end
end
Kind of late, but maybe this can helps others as helping me.
My answer here
I guess this resolve the 2 and 3 fails.
Regards

Mongoid querying

I have some application on RoR with Mongodb database. I use Mongoid mapper. Model post.rb
class Post
include Mongoid::Document
field :title, :type => String
field :text, :type => String
embeds_many :comments
end
Model comment.rb
class Comment
include Mongoid::Document
field :name, :type => String
field :content, :type => String
embedded_in :post, :inverse_of => :comments
end
In database this post with some comment has next structure:
{
"_id": ObjectId("4ecbeacf65430f0cef000003"),
"comments": {
"0": {
"name": "my name",
"content": "example content",
"_id": ObjectId("4ecbead365430f0cef000005")
},
"1": {
"name": "some name",
"content": "example content",
"_id": ObjectId("4ecbead665430f0cef000007")
},
"2": {
"name": "some name",
"content": "example content",
"_id": ObjectId("4ecbeada65430f0cef000009")
}
},
"text": "example text",
"title": "example title"
}
And, for example, in database was a few posts with my comments.
I need to find all posts, where "name": "my name", i.e. I need to find all editable by me posts.
It should appear as a array of comments instead of a hash. See my example below.
Also, as per the mongoid docs use the new style field declarations.
comment.rb:
class Comment
include Mongoid::Document
field :name, type: String
field :content, type: String
embedded_in :post
end
post.rb:
class Post
include Mongoid::Document
field :title, type: String
field :text, type: String
embeds_many :comments
end
Rails Console:
p = Post.new(:title => "title", :text => "post")
c1 = Comment.new(:name => "Tyler", :comment => "Awesome Comment!")
c2 = Comment.new(:name => "Joe", :comment => "comment 2")
p.comments << c1
p.comments << c2
p.save
Mongo Console:
> db.posts.findOne()
{
"_id" : ObjectId("4ecd151d096f762149000001"),
"title" : "title",
"text" : "post body",
"comments" : [
{
"name" : "Tyler",
"comment" : "Awesome Comment!",
"_id" : ObjectId("4ecd1569096f762149000002")
},
{
"name" : "Joe",
"comment" : "comment 2",
"_id" : ObjectId("4ecd157f096f762149000003")
}
]}
Then, to do the query you want, which I think was "comments posted by me?":
> db.posts.findOne({"comments.name": "Tyler"})
Also, I would add an index to the comments.name field:
> db.posts.ensureIndex({"comments.name": 1})