I'm writing a simple Rails api that connects to a legacy SQL Server database. I am testing my REST actions for my contacts controller. When using FactoryGirl to create test objects, I ran into the error message mentioned in the title. My index and show actions work fine, but the create action is throwing this error. The relevant parts of my contacts_controller look like this:
def create
contact = Contact.new(contact_params)
if contact.save
render json: contact, status: 201, location: [:api, contact]
else
render json: { errors: contact.errors }, status: 422
end
end
...
private
def contact_params
params.require(:contact).permit(:name, :address_1, :city, :zip_code_5, :country)
end
And here is the relevant test code:
describe "POST #create" do
context "when is successfully created" do
before(:each) do
#user = FactoryGirl.create :user
#contact = FactoryGirl.create :contact
post :create, { contact: #contact }
end
it "renders the json representation for the contact record just created" do
contact_response = json_response
expect(contact_response[:name]).to eq #contact_attributes[:name]
end
it { should respond_with 201 }
end
end
The model:
class Contact < ActiveRecord::Base
belongs_to :user
validates :name, :address_1, :city, :zip_code_5, :country, :createddate, presence: true
end
The serializer (using the active_model_serializer gem):
class ContactSerializer < ActiveModel::Serializer
belongs_to :user
attributes :id, :name, :address_1, :city, :zip_code_5, :country
end
Things I've tried include:
Changing the 'belongs_to' to 'has_one' in the serializer (no change)
Removing the 'zip_code_5' from the permite...require line (strangely, I still got the error message about this property, perhaps because of the serializer?)
Removing the serializer (no change)
Any thoughts? I'm happy to provide any more necessary information.
EDIT
The value of #contact when it's passed to the create action:
#<Contact id: 89815, user_id: "d67b0d57-8f7f-4854-95b5-f07105741fa8", title: nil, firstname: nil, lastname: nil, name: "Alene Stark", company: nil, address_1: "72885 Bauch Island", address_2: nil, address_3: nil, city: "Joestad", state: nil, zip_code_5: "98117", zip_code_4: nil, country: "MF", status_id: 1, createddate: "2015-10-23 07:00:00", lastmodifieddate: "2012-11-29 08:00:00", errorreasonid: nil, computergenerated: true, sandbox: true, emailsubject: nil, jobtitle: nil, mergevar1: nil, mergevar2: nil, mergevar3: nil, mergevar4: nil, mergevar5: nil, mergevar6: nil, mergevar7: nil, mergevar8: nil, mergevar9: nil, mergevar10: nil, clientid: 1, isshared: true>
The value of params[:contact] at runtime:
{"city"=>"Seattle", "state"=>"WA", "zip_code_5"=>"98117", "country"=>"US"}
I also have my wrap parameters set to :json format, if that's relevant.
I used the console to recreate what my test was doing. I discovered that Contact was being passed as a string, instead of a hash. After a little Googling, I passed the #contact object as #contact.attributes, which passes a hash of the object. This solved the 'permit' problem, thanks for pointing me in the right direction.
Related
I have an object (Institution) and I want to get the 2 arrays (marks and attachments) that are relationed with this object using JSON.
To be clear: For 1 institution, I have 3 marks and for every mark I have an attachment.
Here's my code of JSON file:
if #data['admin_institution']
json.extract! #data['admin_institution'], :id, :name, :phone, :address, :site, :created_at, :updated_at
if #data['admin_institution'].marks
json.marks #data['admin_institution'].marks
json.array!(#data['admin_institution'].marks) do | admin_mark|
json.attachment admin_mark.attachment
end
end
else
json.set! :response do
json.set! :error, 'Not Found!'
end
end
I want to reproduce something like this:
{
id: 14,
name: "Ins3",
phone: "793215-2555",
address: "lreewrwklkr",
site: "lkerlke.com",
created_at: "2016-03-01T14:00:37.000-03:00",
updated_at: "2016-03-01T14:00:37.000-03:00",
- marks: [
- {
id: 17,
admin_attachment_id: 927,
admin_bookmark_id: 3,
admin_institution_id: 14,
created_at: "2016-03-01T14:00:37.000-03:00",
updated_at: "2016-03-01T14:00:37.000-03:00"
},
{
id: 18,
admin_attachment_id: 945,
admin_bookmark_id: 1,
admin_institution_id: 14,
created_at: "2016-03-01T14:00:37.000-03:00",
updated_at: "2016-03-01T14:00:37.000-03:00"
}
],
- attachment: {
id: 927,
name: "nature-16",
title: "Nature-16",
description: null,
mime_type: "image/jpeg",
url: "/uploads/nature-16.jpg",
created_at: "2016-02-29T09:21:09.000-03:00",
updated_at: "2016-02-29T09:21:09.000-03:00"
}
}
Instead, I'm getting only the values of the last array (attachment). Thanks in advance.
UPDATE:
I used through-association on my institution model then I could get the attachments "directly" through the marks, without do a loop of marks. The following code is giving me almost all that I want.
if #data['admin_institution']
json.extract! #data['admin_institution'], :id, :name, :phone, :address, :site, :created_at, :updated_at, :marks, :attachments
else
json.set! :response do
json.set! :error, 'Not Found!'
end
end
It's returning the Institution, the marks and the attachments, BUT not nested. I want marks inside the institution and attachments inside the marks. How can I make it work?
Try this:
if #data['admin_institution']
...
if #data['admin_institution'].marks
json.marks #data['admin_institution'].marks do | admin_mark|
json.attachment admin_mark.attachment
end
end
else
...
end
I have two models: Cabinet and Workplace.
class Cabinet < ActiveRecord::Base
def as_json(options={})
options.merge!({except: [:created_at, :updated_at]})
super(options)
end
end
class Workplace < ActiveRecord::Base
belongs_to :cabinet
def as_json(options = {})
options.merge!(:except => [:created_at, :updated_at, :cabinet_id], include: :cabinet)
super(options)
end
end
When I called Cabinet.first.to_json I get
{
id: 1,
cabinet: "100"
}
but when I called Workplace.first.to_json id get
{
name: "first workplace",
Cabinet: {
id: 1,
cabinet: "100",
created_at: "#created_at",
updated_at: "#updated_at"
}
}
Why this? Thanks and sorry for my english :)
Not sure if I am following you, but do you want to get just attributes from Workplace model, and not Cabinet data when you do Workplace.first.to_json?
I think it is because you include cabinet in as_json method configuration as explained here.
You should either remove it or do this:
Workplace.first.attributes.to_json
Let me know if I am missing something from your question.
Let's assume that your model Cabinet has :id, :cabinet, :created_at, :updated_at attributes and Workplace has :id, :name, :cabinet_id, .....
Now, if you try to fire Cabinet.first.to_json, ofcourse it will render the following:
{
id: 1,
cabinet: "100"
}
becuase that is the attributes belongs to Cabinet model. Then you also added these line of code options.merge!({except: [:created_at, :updated_at]}) that's why it only renders :id and :name attributes. And if you try to fire Workplace.first.to_json then it will render:
{
name: "first workplace",
Cabinet: {
id: 1,
cabinet: "100",
created_at: "#created_at",
updated_at: "#updated_at"
}
}
because, of these options.merge!(:except => [:created_at, :updated_at, :cabinet_id], include: :cabinet). You include the model Cabinet so it will automatically added to your json.
So in my wiki model I have an attribute for private. If private is true then the wiki should not be viewable to users who are not assign to the wiki_ids via a HABTM relationship.
wiki.rb:
class Wiki
include Mongoid::Document
include Mongoid::Timestamps
has_and_belongs_to_many :users
field :title, type: String
field :body, type: String
field :private, type: Boolean, default: false
scope :visible_to, ->(user) {
user.present? || user.blank? ?
where(:private => false) : where(:private => false).or(:id => user.wiki_ids)
}
def public?
!self.private?
end
end
WikisController:
def index
##wikis = policy_scope(Wiki)
##wikis = Wiki.all
#wikis = Wiki.visible_to(current_user)
authorize #wikis
end
def show
#wiki = Wiki.find(params[:id])
end
def new
#wiki = Wiki.new
authorize #wiki
end
def create
#wiki = current_user.wikis.build(params.require(:wiki).permit(:title, :body, :private, :user))
authorize #wiki
if #wiki.save
flash[:notice] = "Wiki was saved."
redirect_to #wiki
# report success
else
flash[:error] = "There was an error saving your wiki. Please try again."
render :new
end
I'm pretty confident its the scope that needs to be modified in the model, because if i comment out the scope in the model and replace the index in the controler to Wiki.all. I see all the wikis.
As of right now as somebody who created the wiki plus flagged it private and I am logged in I do not see that wiki nor does anybody that I add as a user to the wiki.
I tried adding other conditions to the end such as user.present? ? where(:id => user.wiki_ids) and user.present? && where(:id => user.wiki_ids) but just get errors thrown back at me.
DB entry for User:
User_id: 547eb8867261691268000000, wiki_ids: [BSON::ObjectId('54807226726 1690be0260000'),
BSON::ObjectId('5480735c7261690bae000000'), BSON::ObjectId('548
136e57261690aef000000'), BSON::ObjectId('5489af337261690d95000000'),
BSON::Objec tId('5489b57e7261690d95010000'),
BSON::ObjectId('548f9f607261690bb5060000'), BSO
N::ObjectId('54908f127261690be8000000'),
BSON::ObjectId('54908f207261690be801000 0')], name: "Carey VonRueden",
email: "admin#email.com", encrypted_password: "$2a
$10$NrlQ2XH64UucOPcI1aje9.57eoSO74676264YrIjfGvncyGcpGWy",
reset_password_token : nil, reset_password_sent_at: nil,
remember_created_at: nil, sign_in_count: 7, current_sign_in_at:
2014-12-17 18:51:15 UTC, last_sign_in_at: 2014-12-16 02:38:5 8 UTC,
current_sign_in_ip: "10.0.2.2", last_sign_in_ip: "10.0.2.2",
confirmation
_token: nil, confirmed_at: 2014-12-03 07:15:18 UTC, confirmation_sent_at: nil, u nconfirmed_email: nil, role: "admin">
DB entry for Wiki:
Wiki _id: 54908f207261690be8010000, created_at: 2014-12-16 19:59:28 UTC, updated_at: 2014-12-16 19:59:28 UTC, user_ids:
[BSON::ObjectId('547eb886726169126 8000000')], title: "Private", body:
"Private", private: true>
your scope condition is wrong
user.present? || user.blank? -> this will be true always. if user is present or user is blank, it will always return only the public wikis
Change your scope to something like below.(assuming you want all public wiki's if user is not signed in. If user is signed in, you want public + the wikis created by user)
scope :visible_to, ->(user) {
user.nil? ? where(:private => false) : where(:private => false).or(:id => user.wiki_ids)
}
If you are still not getting what you are expecting, check if user.wiki_ids is returning the right values
I'm following the Treebook tutorial on teamtreehouse.com (making a facebook clone) using rails. I have created a userfriendships controller and am getting this error when trying to reference a friend's full name in my index.html page of the friendships view: undefined method `full_name' for nil:NilClass. The problem occurs on line 4 here, (this is /views/user_friendships/index.html)
<div class="page-header">
<h1>Friends</h1>
</div>
<hr />
<% if #user_friendships.empty? %>
<em>No <%= params[:list] %> friends yet!</em>
<% end %>
<% #user_friendships.each do |friendship| %>
<% friend = friendship.friend %>
<div id="<%= dom_id(friendship) %>" class="friend panel">
<div class="panel-heading">
<span class="pull-right label>"><%= "#{friendship.state}".upcase %></span>
<h3 class="panel-title"><%= friend.full_name %></h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-1">Put gravatar url here</div>
<div class="col-md-11">
<em>Friends since time_ago_in_words ago</em>
</div>
</div>
</div>
<div class="panel-footer clearfix">
<%= link_to "Update friendship", edit_user_friendship_path( friend.profile_name), class: 'btn btn-primary pull-right' %>
</div>
</div>
<% end %>
Here's the User Friendships Controller:
class UserFriendshipsController < ApplicationController
before_filter :authenticate_user!
def index
#user_friendships = current_user.user_friendships.all
end
def new
if params[:friend_id]
#friend = User.where(profile_name: params[:friend_id]).first
raise ActiveRecord::RecordNotFound if #friend.nil?
#user_friendship = current_user.user_friendships.new(friend: #friend)
else
flash[:error] = "Friend required"
end
rescue ActiveRecord::RecordNotFound
render file: 'public/404', status: :not_found
end
def create
if params[:user_friendship] && params[:user_friendship].has_key?(:friend_id)
#friend = User.where(profile_name: params[:user_friendship][:friend_id]).first
#user_friendship = UserFriendship.request(current_user, #friend)
respond_to do |format|
if #user_friendship.new_record?
format.html do
flash[:error] = "There was a problem creating that friend request."
redirect_to profile_path(#friend)
end
format.json { render json: #user_friendship.to_json, status: :precondition_failed }
else
format.html do
flash[:success] = "Friend request sent to #{#friend.full_name}."
redirect_to profile_path(#friend.profile_name)
end
format.json { render json: #user_friendship.to_json }
end
end
else
flash[:error] = "Friend required"
redirect_to root_path
end
end
def edit
#friend = User.where(profile_name: params[:id]).first
#user_friendship = current_user.user_friendships.find(params[:id])
end
def destroy
#user_friendship = current_user.user_friendships.find(params[:id])
if #user_friendship.destroy
flash[:success] = "Friendship destroyed."
end
redirect_to user_friendships_path
end
def accept
#user_friendship = current_user.user_friendships.find(params[:id])
if #user_friendship.accept!
flash[:success] = "You are now friend with #{#user_friendship.friend.full_name}"
else
flash[:error] = "That friendship could not be accepted."
end
redirect_to user_friendships_path
end
def block
#user_friendship = current_user.user_friendships.find(params[:id])
if #user_friendship.block!
flash[:success] = "You have blocked #{#user_friendship.friend.full_name}."
else
flash[:error] = "That friendship could not be blocked."
end
redirect_to user_friendships_path
end
private
def friendship_association
case params[:list]
when nil
current_user.user_friendships
when 'blocked'
current_user.blocked_user_friendships
when 'pending'
current_user.pending_user_friendships
when 'requested'
current_user.requested_user_friendships
when 'accepted'
current_user.accepted_user_friendships
end
end
private
def user_friendship_attributes
params.require(:user_friendship).permit(:user, :friend, :user_id, :friend_id, :state)
end
end
Lastly, I will include the user friendships model:
class UserFriendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, class_name: 'User', foreign_key: 'friend_id'
state_machine :state, initial: :pending do
after_transition on: :accept, do: :send_acceptance_email
state :requested
event :accept do
transition any => :accepted
end
end
def self.request(user1, user2)
transaction do
friendship1 = create(user: user1, friend: user2, state: 'pending')
friendship2 = create(user: user2, friend: user1, state: 'requested')
friendship1.send_request_email
friendship1
end
end
def send_request_email
UserNotifier.friend_requested(id).deliver
end
def send_acceptance_email
UserNotifier.friend_request_accepted(id).deliver
end
def mutual_friendship
self.class.where({user_id: friend_id, friend_id: user_id}).first
end
# Manually updating the state to avoid callbacks and infinite loops
def accept_mutual_friendship
mutual_friendship.update_attribute(:state, 'accepted')
end
end
I'm aware that I must have to change this somehow, but am at a loss as to how to do that. I would like this page to display all of the friends that a user has, either in an accepted or pending state. Can anyone see what I need to do?
Thanks!
EDIT: If I change the views/user_friendships/index.html to friend.full_name instead of #friend.full_name I still get the same error. I can verify that the users currently in the db all have a valid first and last name (so the full name method should work).
Another EDIT: Here is a rails console entry showing that I do have userfriendships in the database:
2.1.2 :003 > UserFriendship.all
UserFriendship Load (0.3ms) SELECT "user_friendships".* FROM "user_friendships"
=> #<ActiveRecord::Relation [#<UserFriendship id: 1, user_id: 6, friend_id: nil, created_at: "2014-10-04 14:20:42", updated_at: "2014-10-04 14:20:42", state: nil>, #<UserFriendship id: 2, user_id: 6, friend_id: nil, created_at: "2014-10-04 14:21:32", updated_at: "2014-10-04 14:21:32", state: nil>, #<UserFriendship id: 3, user_id: 6, friend_id: 3, created_at: "2014-10-04 14:21:50", updated_at: "2014-10-04 14:21:50", state: nil>, #<UserFriendship id: 4, user_id: 6, friend_id: 6, created_at: "2014-10-04 14:23:58", updated_at: "2014-10-04 14:23:58", state: nil>, #<UserFriendship id: 5, user_id: 6, friend_id: 6, created_at: "2014-10-04 14:25:05", updated_at: "2014-10-04 14:25:05", state: nil>, #<UserFriendship id: 6, user_id: 6, friend_id: 4, created_at: "2014-10-04 14:25:15", updated_at: "2014-10-04 14:25:15", state: nil>, #<UserFriendship id: 7, user_id: 6, friend_id: 6, created_at: "2014-10-04 20:42:40", updated_at: "2014-10-04 20:42:40", state: nil>, #<UserFriendship id: 8, user_id: 6, friend_id: 6, created_at: "2014-10-04 20:45:44", updated_at: "2014-10-04 20:45:44", state: nil>, #<UserFriendship id: 9, user_id: 6, friend_id: 6, created_at: "2014-10-04 21:38:17", updated_at: "2014-10-04 21:38:17", state: nil>, #<UserFriendship id: 10, user_id: 6, friend_id: 6, created_at: "2014-10-09 16:53:44", updated_at: "2014-10-09 16:53:44", state: "pending">, ...]>
2.1.2 :004 >
Another Edit: Here is the user model models/user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :first_name, presence: true
validates :last_name, presence: true
validates :profile_name, presence: true,
uniqueness: true,
format: {
with: /\A.+\z/,
message: "must be formatted correctly"
}
has_many :statuses
has_many :user_friendships
has_many :friends, -> { where user_friendships: { state: 'accepted'} }, through: :user_friendships
has_many :pending_user_friendships, -> { where state: 'pending' },
class_name: 'UserFriendship',
foreign_key: :user_id
has_many :pending_friends, through: :pending_user_friendships, source: :friend
def full_name
first_name + " " + last_name
end
def gravatar_url
stripped_email = email.strip
downcased_email = stripped_email.downcase
hash = Digest::MD5.hexdigest(downcased_email)
"http://gravatar.com/avatar/#{hash}"
end
end
instead of #friend.full_name use friend.full_name
You have define it here <% friend = friendship.friend %>
So I am pretty sure what happened was that some of my database tables were dropped during a rake db:migrate that I ran. I did it because I was looking at a similar rails app that was building a social network site and switching my database back and forth between the two. Not sure how or why migrate deleted something ( i believe it deleted first and last name which is why full_name would be an nil error). I am going to revert to a previous commit where everything was working and try to rebuild from there.
Thanks for the posts!
tl;dr: dont run db:migrate without knowing what you're doing
Code
In my image model:
has_attached_file :pic
before_post_process :rename_pic
before_save ->{ p 'before_save ----------------' }
after_post_process ->{ p 'after_post_process --------------' }
def rename_pic
p 'en imagen'
p self
p 'en imagen'
end
In service that has many images:
# don't use accepts_nested_attributes_for
before_save :create_images
attr_accessor :images_attributes
def create_images
# images_attributes example value: { "0"=> {img_attrs}, "1" => {img_attrs1} }
images_attributes.all? do |k, image_attrs|
if image_attrs.delete(:_destroy) == "false"
p 'asd'
image = Image.new image_attrs.merge(service_id: id)
p image.service
p image.service_id
image.save
end
end
end
This is the output I get:
"asd"
"en imagen"
#<Image id: nil, service_id: nil, pic_file_name: "Screen_Shot_2013-04-07_at_5.18.03_PM.png", pic_content_type: "image/png", pic_file_size: 16041, pic_updated_at: "2013-07-30 22:58:46", created_at: nil, updated_at: nil, user_id: nil>
"en imagen"
G"after_post_process --------------"
#<Service id: 427, event_id: nil, min_capacity: nil, max_capacity: nil, price: #<BigDecimal:7fb6e9d73d48,'0.0',9(18)>, image_path: nil, name: "Super Franks", desc: "zxc", created_at: "2013-05-12 19:01:54", updated_at: "2013-07-30 19:32:48", address: "pasadena", longitude: 77.225, latitude: 28.6353, gmaps: true, city: "san francisco", state: "california", country_id: "472", tags: "Banquet", created_by: 22, avg_rating: #<BigDecimal:7fb6efdbcf10,'0.0',9(18)>, views: 27, zip_code: "", address2: "", price_unit: "", category_id: 3, featured: true, publish: true, slug: "banquet-super-franks", discount: nil, currency_code: "USD", video_url: "http://www.youtube.com/watch?v=A3pIrBZQJvE", short_description: "">
427
"before_save ----------------"
Problem
When calling
image = Image.new image_attrs.merge(service_id: id)
Paperclips seems to start processing, and then set service_id.
So when I try to use service inside rename_pic service is nil.
Any ideas on how to handle this?
This solved my problem, I changed:
before_post_process :rename_pic
to:
before_create :rename_pic
and this is rename_pic, for the record:
def rename_pic
extension = File.extname(pic_file_name).downcase
self.pic.instance_write :file_name,
"#{service.hyphenated_for_seo}#{extension}"
end
where service has_many images, and image belongs_to service.
Be carefull with the fix of #juanpastas, because if you change before_post_process to before_create, it will only run when you create your image, and not when you update it. To have the callback still run on update, do this:
class YourImage
has_attached_file :pic
# use both callbacks
before_create :rename_pic
before_post_process :rename_pic
def rename_pic
# assotiated_object is the association used to get pic_file_name
return if self.assotiated_object.nil?
extension = File.extname(pic_file_name).downcase
self.pic.instance_write :file_name,
"#{service.hyphenated_for_seo}#{extension}"
end
end