Mixing GlobalID::Identification for form object - ruby-on-rails

Rails 4.2.5
I have Form object Feedback.
class Feedback
include ActiveModel::Model
attr_accessor :name, :message
validates :name, :message, presence: true
end
And controller FeedbacksController
def create
#feedback = Feedback.new(feedback_params)
if #feedback.valid?
FeedbackServiceMailer.new_feedback(#feedback).deliver_later
redirect_to root_url, notice: 'Thanks for your feedback!'
else
render :index, status: 400
end
end
To be able to pass #feedback object to Mailer. I have added GlobalID::Identification mixin and provided id and self.find methods:
def id
object_id
end
def self.find(id)
ObjectSpace._id2ref(id.to_i)
end
My question, is it okay to provide object_id for deserializing Form object?
As I understood Ruby GC will not collect #feedback object if something have a reference to it.

Related

Rails: Creating new model based on a parameter in another model's controller

I'm trying to create a model where every blog post has a number of tags (a separate model) associated to it. In my form, I pass back a separated string of tags along with the post, like "apples,oranges,bananas". I'm eventually going to try and split this string and then create a tag for each string. However, for now I'm just trying to see if I can make one tag with the whole string.
The request is handled by the create method of the posts controller. However, I can't figure out how to create and commit the tag in the posts controller, or at least call the tags controller to delegate the creation. I get the error:
undefined method `new=' for Tag(id: integer, tag_name: string, post_id: integer):Class
This points to the line Tag.new = #post.tags.create(post_params[:tag_name]) of the posts_controller.
Relevant code:
posts_controller
def new
#post = Post.new
end
def create
#post = current_user.posts.create(post_params)
Tag.new = #post.tags.create(post_params[:tag_name])
if #post.save
redirect_to post_path(#post), :notice => "Post was created successfully."
else
render :new
end
end
private
def post_params
params.require(:post).permit(:title, :content_md, :image, :user_id, :year_created, :medium, :dimension_height, :dimension_width, :measurement, :weight_in_pounds, :price, :quantity)
end
tags_controller
class TagsController < ApplicationController
def new
#tag = Tag.new
end
def create
#tag = tag.create(tag_params)
end
private
def tag_params
params.require().permit(:tag_name, :user_id)
end
end
post.rb
class Post < ActiveRecord::Base
# Relations
belongs_to :user
has_many :comments
has_many :post_votes
has_many :tags
accepts_nested_attributes_for :tags
end
tag.rb
class Tag < ActiveRecord::Base
validates :tag_name, presence: true
validates :post_id, presence: true
belongs_to :post
end
Edit:
Also tried:
def create
#tag = Tag.new(post_params[:tag_name])
#tag.save
#post = current_user.posts.create(post_params)
# #post.tags.create(post_params[:tag_name])
if #post.save
redirect_to post_path(#post), :notice => "Post was created successfully."
else
render :new
end
end
for the posts controller. It creates the post, but doesn't seem to actually save the #tag
Without going into much deep, I see you you've the following line in your code (in posts_controller)
Tag.new = #post.tags.create(post_params[:tag_name])
which is not correct. If you want to just create (but not persist) you should call,
#post.tags.new(post_params[:tag_name])
For persist
#post.tags.create(post_params[:tag_name])
This will get rid of the error that you've posted. However, you shouldn't require these as your Post model can accept nested attributes for tags. Just make sure you've generated form correctly (hint: fields_for)
This line
Tag.new = #post.tags.create(post_params[:tag_name])
has no sense. Without further discussion:
#post.tags.create(post_params[:tag_name])
will work and actually create the tag.

Instantiate a ruby class no initialize method but with attr_accesors?

The following code works but I don't understand why.
The Model: I have a Class called Contact that doesn't have an initialize method (i.e it inherits the initialize method from the default Object class).
class Contact
include ActiveModel::Model
attr_accessor :name, :string
attr_accessor :email, :string
attr_accessor :content, :string
validates_presence_of :name
validates_presence_of :email
validates_presence_of :content
...
end
The controller: I have a ContactsController with a 'create' method that instantiates the Contact class passing along some parameters through the 'secure_params' method.
class ContactsController < ApplicationController
def new
#contact = Contact.new
end
def create
# THIS IS THE LINE THAT I DON'T UNDERSTAND
#contact = Contact.new(secure_params)
if #contact.valid?
#contact.update_spreadsheet
UserMailer.contact_email(#contact).deliver
flash[:notice] = "Message sent from #{#contact.name}."
redirect_to root_path
else
render :new
end
end
private
def secure_params
params.require(:contact).permit(:name, :email, :content)
end
end
Where do this parameters go to if there is no initialize method that sets them to instance variables and the default behavior of the 'new' method (inherited from the Ruby's Object class) does nothing with passed in parameters?
Why do they end up being set as instance variables? (something to do with the attr_accesors?)
You are including ActiveModel::Model which defines the initialize method that sets the values.

Rails Validates don't work on update

I've got a problem with validators. I have a "contact" model which contains two fields firstname and lastname and I want both required on CREATE and UPDATE method. When I create a record with no data, the server return me a 422 and do the rollback. This is ok. But when I update a record the server don't return the error 422 although the server does the rollback. And I need the return of the error to manage it on the client side.
So I use validators like this :
class Contact < ActiveRecord::Base
validates :lastname, presence: true
validates :firstname, presence: true
end
and my controller is:
class ContactsController < ApplicationController
respond_to :json
def index
respond_with Contact.all
end
def create
respond_with Contact.create(contact_params)
end
def show
respond_with Contact.find(params[:id])
end
def edit
respond_with Contact.find(params[:id])
end
def update
respond_with Contact.find(params[:id]).update(contact_params)
end
def destroy
respond_with Contact.find(params[:id]).destroy
end
private
def contact_params
params.require(:contact).permit(:lastname, :firstname, :position)
end
end
I have a serializer:
class ContactSerializer < ActiveModel::Serializer
attributes :id, :lastname, :firstname, :created_at, :updated_at
end
Someone could help me, please ?
Thanks by advance.
Contact.find(params[:id]).update(contact_params)
returns a Boolean, hence you are telling Rails to render a boolean (which will render a 200 with the boolean serialized as JSON).
Same for destroy. You need to pass the instance.
def update
contact = Contact.find(params[:id])
contact.update(contact_params)
respond_with contact
end
def destroy
contact = Contact.find(params[:id])
contact.destroy
respond_with contact
end
It's also a good habit to extract the finder in a before_action.
before_action :find_contact
def update
#contact.update(contact_params)
respond_with #contact
end
def destroy
#contact.destroy
respond_with #contact
end
protected
def find_contact
#contact = Contact.find(params[:id])
end
You can refactor the other actions to remove the duplicate finder.
you can try the following code, it could be tempo fix
def update
#contact = Contact.find params[:id]
#contact.update contact_params
if #contact.errors.count > 0
# do you like
else
respond_with #contact
end
end

Rails password can't be blank error

I am trying to create a user signup form in Rails 4.1.6. I keep getting a 'password can't be blank' error. I can see that both the password and password_confirmation are in the params hash but not in the params[:user] sub-hash. I cannot figure out why for the life of me. Any help will be greatly appreciated.
User Model:
class User < ActiveRecord::Base
belongs_to :company
has_secure_password
end
Users Controller:
class UsersController < ApplicationController
respond_to :json
def index
#users = User.all
respond_with(#users)
end
def create
#user = User.new(user_params)
#user.save
respond_with(#user)
end
private
def user_params
params.require(:user).permit(:given_name, :family_name, :email, :password, :password_confirmation)
end
end
It sounds like your parameters are not being sent to the server correctly.
password should be sent as user[password] and password_confirmation should be sent as user[password_confirmation].
See documentation for hash and array parameters.
Alternatively, adding wrap_parameters to the controller will wrap parameters into a nested hash.
wrap_parameters :user, include: [:given_name, :family_name, :email, :password, :password_confirmation]
See documentation for ActionController::ParamsWrapper.

Acts_as_commentable_with_threading - comments are not adding

#post.comments.all is clear. and i dont see any errors after i send form. When i click "Submit" i sent to posts/id/comments, but
my routes.rb
resources :posts do
resources :comments
end
post controller
def show
#current_user ||= User.find(session[:user_id]) if session[:user_id]
#commenter = #current_user
#post = Post.find(params[:id])
#comment = Comment.build_from( #post, #commenter.id, "234234" )
#comments = Comment.all
respond_to do |format|
format.html
format.json { render json: #post }
end
end
comments controller
class CommentsController < ApplicationController
def index
#comments = #post.comments.all
end
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.new params[:comment]
if #comment.save
redirect_to #post # comment not save, so i dont redirect to this page
else
# is that there
end
end
end
post model
acts_as_commentable
has_many :comments
comment model
class Comment < ActiveRecord::Base
acts_as_nested_set :scope => [:commentable_id, :commentable_type]
attr_accessible :commentable, :body, :user_id
validates :body, :presence => true
validates :user, :presence => true
# NOTE: install the acts_as_votable plugin if you
# want user to vote on the quality of comments.
#acts_as_votable
belongs_to :commentable, :polymorphic => true
# NOTE: Comments belong to a user
belongs_to :user
# Helper class method that allows you to build a comment
# by passing a commentable object, a user_id, and comment text
# example in readme
def self.build_from(obj, user_id, comment)
new \
:commentable => obj,
:body => comment,
:user_id => user_id
end
#helper method to check if a comment has children
def has_children?
self.children.any?
end
# Helper class method to lookup all comments assigned
# to all commentable types for a given user.
scope :find_comments_by_user, lambda { |user|
where(:user_id => user.id).order('created_at DESC')
}
# Helper class method to look up all comments for
# commentable class name and commentable id.
scope :find_comments_for_commentable, lambda { |commentable_str, commentable_id|
where(:commentable_type => commentable_str.to_s, :commentable_id => commentable_id).order('created_at DESC')
}
# Helper class method to look up a commentable object
# given the commentable class name and id
def self.find_commentable(commentable_str, commentable_id)
commentable_str.constantize.find(commentable_id)
end
end
post view
%h2 Add a comment:
- #comments.each do |c|
= #c.body
= form_for([#post, #comment]) do |f|
.field
= f.label :body
%br/
= f.text_area :body
.actions
= f.submit
Thanks in advance and sorry for bad english
First of all you can debug why #comment.save return false yourself - just add p #comment.errors in else block and check server log.
It seems for me that you try to save invalid comments because you don't have setup user for #comment in action CommentsController#create. Comment validates presence of user!
There are several ways how to fix it. Analyzing your code I think the simplest way for you is modify CommentsController#create
#CommentsController
def create
#current_user ||= User.find(session[:user_id]) if session[:user_id]
#post = Post.find(params[:post_id])
#comment = #post.comments.new params[:comment]
#comment.user = #current_user
if #comment.save
redirect_to #post # comment not save, so i dont redirect to this page
else
# is that there
end
end
Another way is to use some gem for authentication - I recommend devise
One more way (very bad way) is to pass user_id through hidden field (you have defined #current_user in PostsController#show and user_id in attr_accessible list in Comment). But this is easy way to hack your application and write comments on behalf of any user in system!

Resources