How do I not use current_user in the model - ruby-on-rails

I have a form to create posts. This form also has tags on the post and it is owned by a user. The form is using a virtual attribute :tag_ids.
I would like to do something like this, where you find_or_create_by_name_and_user_id and then just pass the current_user.id.
def tag_ids=(tags_string)
self.taggings.destroy_all
tag_names = tags_string.split(",").collect{|s| s.strip.downcase}.uniq
tag_names.each do |tag_name|
tag = Tag.find_or_create_by_name_and_user_id(tag_name, current_user.id)
tagging = self.taggings.new
tagging.tag_id = tag.id
end
end
From what I read this isn't how it should work. The current user should be called in the PostsContoller create method.
def create
#post = Post.new(params[:post])
#post.user = current_user
respond_to do |format|
if params[:commit]
#post.save
...
end
This is where I'm stuck. If the params are passing all of the attributes, how can I inject the current_user id into tag model which has a :name and :user_id attribute?
I also read that this should work because of my associations.
User - has_many posts; has_many tags
Post - belongs_to user; has_many taggings; has_many tags through taggings
Tag - has many taggings; has_many posts through taggings; belongs_to user
Tagging - belongs_to post; belongs_to tag

You can't get current_user in model ,you have to pass explicitly,there are may be couple of hack to do this,But simple way is,user is already stored in post so self.user.id is useful
If above method is in post model,post user is tagging user.
Tag.find_or_create_by_name_and_user_id(tag_name, self.user.id)

Related

How to add user_id in form params in Rails controller

I have this model, called products, that has an id, user_id and product_name.
I have a simple form_for to pass to my product_controller the product_name param. In this same controller, I have access to the current_user, which I have to pass in order to create a new Product.
The problem is that the user_id always has to be the current user logged in, so I can't allow the user to send me this param in my form_for, thus I can't permit it as well.
What I'm doing right now is to create a new hash with the user_id param, and merge the params that comes from the form.
products_controller.rb:
def create
product_user_id_param = { "user_id": current_user.id.to_s }
#product = Product.new(product_user_id_param.merge(params[:product_name]))
...
end
Is there a more convenient or Rails way to do this?
You can create you product with you current_user reference, this is the more convenient way in my opinion:
current_user.produtcs.create(name: params[:product_name])
But, to above code works, you have to construct you relations correctly, like the following:
class User < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belogs_to :user
end
Now, you can do it!
You can read more about it, in https://guides.rubyonrails.org/v3.2/association_basics.html, the docs recommeds this way!
Solution #1
Product has user_id which means product belongs_to :user and user has_many :products
Hence it can be written as
current_user.products.new(product_name: params[:product_name])
Solution#2
Set current_user explicitly
#product = Product.new(product_name: params[:product_name])
#product.user_id = current_user.id
##product.user = current_user
#product.save

Rails how to associate 2 models on create?

I would like to associate Order object wit Dispute Object on create of Dispute but when i go create the object in the log shows:
ActiveRecord::RecordNotFound (Couldn't find Order without an ID)
should i not try to find the order in the method?
Someone know how to associate the objects in the creation?
the Dispute Controller is:
class DisputesController < ApplicationController
def new
if current_user.address.blank?
redirect_to edit_user_path
flash[:error] = 'fill the address'
else
#dispute = Dispute.new
end
end
def create
#order = Order.find(params[:id])
if current_user == #order.buyer
dispute = #order.dispute.nil? ? Dispute.new : #order.dispute
dispute.attributes = params[:dispute]
dispute.user = #order.buyer
dispute.buyer_name = #order.buyer_name
dispute.seller_name = #order.seller_name
if dispute.save
flash[:success] = 'Dispute Created'
end
end
The order model
class Order < ActiveRecord::Base
has_one :dispute
end
the dispute model
class Dispute < ActiveRecord::Base
belongs_to :order
end
Without adding parameters or a nested route the request won't know what order is being referenced. You can use nested routes like orders/:order_id/dispute (http://guides.rubyonrails.org/routing.html#nested-resources) and then you can use #order.build_dispute (http://guides.rubyonrails.org/association_basics.html#methods-added-by-belongs-to)
My first thought based on the error you are getting is to check what params you have available upon the form's submission, because it seems it is not finding an Order based on the param you're passing into the find call.
Also check out strong params for security: http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html

How can I capture an instance generically?

I'm using Rails 3.2.19 and Ruby 2.1.2. I've been googling around trying to figure this out, but perhaps I'm not searching for the right thing. Anyway, I'll try and be as concise as possible.
I have a few different models that all have a name attribute. In my views I want to somehow be able to access that name attribute regardless of the instance name passed into the view. Currently my various controllers create instances of their respective models. For instance:
class PagesController < ApplicationController
def show
#page = Page.find(params[:id])
respond_to do |format|
format.html
end
end
end
-
class ProductsController < ApplicationController
def show
#product = Product.find(params[:id])
respond_to do |format|
format.html
end
end
end
While I understand I could simply re-name the instances something generic, I was wondering if there was some way of accessing any/all instances while maintaining unambiguous instance names.
Basically something like this:
page.html.haml
%h1= resources[0].name #equates to #page.name
%h2= #page.some_other_attribute
or
product.html.haml
%h1= resources[0].name #equates to #product.name
%h2= #product.price
Where in each of the above resources[0] would be either #page or #product
You will have to define a route with an additional resource_type parameter to a generic controller or otherwise just include the resource_type into the url query parameter
/resources/product/17
or
/resources/17?resource_type=product
This will allow you to do the following in the controller
class ResourcesController < ApplicationController
def show
#resource = find_resource(params)
respond_to do |format|
format.html
end
end
private
def find_resource(params)
resource_klass = {
product: Product,
page: Page
}[params[:resource_type]]
resource_klass.find(params[:id])
end
end
Another Option would be to introduce another ResourceType Entity and define a polymorphic :has_one :belongs_to association to the actual resource entity (product, page). Then always search for ResourceTypes and load the polymorphic resource entity
class ResourceType < ActiveRecord::Base
belongs_to :resource, polymorphic: true
end
class Product < ActiveRecord::Base
has_one :resource_type, as: :resource
end
class Page < ActiveRecord::Base
has_one :resource_type, as: :resource
end
product_resource_type = ResourceType.create(...)
product = Product.create(resource_type: product_resource_type)
page_resource_type = ResourceType.create(...)
page = Page.create(resource_type: page_resource_type)
ResourceType.find(product_resource_type.id).resource
=> product
ResourceType.find(page_resource_type.id).resource
=> page
I figured this out after discovering instance_variables and instance_variables_get
Those methods will return all instance variables being passed into the view. From there I discovered that the :#_assigns instance variable contained the instances that I was looking for. So I iterated over them to find if any had the name attribute.
- instance_variable_get(:#_assigns).each do |var|
- if var[1].respond_to?("name")
%h1= var[1].name
There is probably a better way of accomplishing this, so if anyone has any opinions, they are welcome.

Editing content that is nil

I want to edit a 3 model on has_many. My question is about the controller and how can I access the information
I have the following model
Customer Book Book_Manager
id id id
first description customer_id
last book_id
email visible
password
The relationship his has follow
Customer
has_many book_managers
has_many books :through -> book_managers
Book
has_many book_managers
has_many customer :through -> book_managers
Book_managers
belongs_to :customer
belongs_to :book
When a customer wants to edit the content, i want to be the latest one to be shown if any exist. The query i have at the moment doesn't seem to handle an empty case and not sure how to do it.
#book = current_customer.books.order("created_at DESC").first
How should i declare it in a def edit of the customercontroller??
Update: I want to be able to see my data, unfoturnotly it doesn't seem to work here my
customer controller
def create
#customer = Customer.new(params[:customer])
#book = #customer.books.build(params[:book])
if #customer.save
cookies[:auth_token] = #customer.auth_token
redirect_to #customer, notice: "Thank you for signing up!"
else
render "new"
end
end
def edit
#customer = Customer.find(params[:id])
if current_customer.books.length > 0
#book = current_customer.books.order("created_at DESC").first
else
#Nor books were found, so create one that has the proper relationship to current_customer
#book = current_customer.books.build
end
end
I am rendering a partial from the book folder, should my create option be in the customer Controller or in bookControllers
Update: using customerController has my main create, when creating its say missing actions in books controller should i have more action in the book controller?
All you need to do is a simple check in the controller to see if any books exist. If one does not exists then create a new one.
#Check if the books array contains anything. There are several ways to do this
if current_customer.books.length > 0
#book = current_customer.books.order("created_at DESC").first
else
#Nor books were found, so create one that has the proper relationship to current_customer
#book = current_customer.books.build
end

Rails 3: alias_method_chain to set specific attribute first

When user's create a post I'd like to set the user_id attribute first. I'm trying to do this using alias_method_chain on the arrtibutes method. But I'm not sure if this is right as the problem I thought this would fix is still occurring. Is this correct?
Edit:
When my users create a post they assign 'artist(s)' to belong to each post, using a virtual attribute called 'artist_tokens'. I store the relationships in an artist model and a joined table of artist_ids and post_ids called artisanships.
I'd like to to also store the user_id of whomever created the artist that belongs to their post (and I want it inside the artist model itself), so I have a user_id column on the artist model.
The problem is when I create the artist for each post and try to insert the user_id of the post creator, the user_id keeps showing as NULL. Which is highly likely because the post's user_id attribute hasn't been set yet.
I figured to get around this I needed to set the user_id attribute of the post first, then let the rest of the attributes be set as they normally are. This is where I found alias_method_chain.
post.rb
attr_reader :artist_tokens
def artist_tokens=(ids)
ids.gsub!(/CREATE_(.+?)_END/) do
Artist.create!(:name => $1, :user_id => self.user_id).id
end
self.artist_ids = ids.split(",")
end
def attributes_with_user_id_first=(attributes = {})
if attributes.include?(:user_id)
self.user_id = attributes.delete(:user_id)
end
self.attributes_without_user_id_first = attributes
end
alias_method_chain :attributes=, :user_id_first
EDIT:
class ArtistsController < ApplicationController
def index
#artists = Artist.where("name like ?", "%#{params[:q]}%")
results = #artists.map(&:attributes)
results << {:name => "Add: #{params[:q]}", :id => "CREATE_#{params[:q]}_END"}
respond_to do |format|
format.html
format.json { render :json => results }
end
end
In your controller, why not just do this:
def create
#post = Post.new :user_id => params[:post][:user_id]
#post.update_attributes params[:post]
...
end
But it seems to me that it would be much better to create the artist records after you've done validation on the post rather than when you first assign the attribute.
EDIT
I would change this to a callback like this:
class Post < ActiveRecord::Base
attr_accessor :author_tokens
def artist_tokens=(tokens)
#artist_tokens = tokens.split(',')
end
after_save :create_artists
def create_artists
#artist_tokens.each do |token|
...
end
end
end

Resources