I'm building a rails app, using devise for authentication and cancan for authorization. I have a form where you can create a "post" and each post has_and_belongs_to many "tags".
I want to create a system for creating tags similar to stack overflow, where the tags are simply inputed via a single text box and then converted to the appropriate tag objects on the server side. Initially I simply had a text box where I could type in a string and the string would be parsed as such in the controller
@post.tags << params[:post][:tags].split(' ').map{ |name| Tag.createOrReturnExisting name}
and that worked perfectly.. until I added cancan authorization, which required me to add
def post_params
params.require(:post).permit(:title,:description,:tags,:content,:types_id)
end
to my controller , which now causes the following error to be thrown upon trying to create a Post undefined method each for "Tag1 Tag2 Tag3\r\n":String
I'm assuming this is because its trying to treat the string from the textbox like an array of tags before I've had a chance to format it.
So my question is, how must I format my controller, model, or view to be able to parse the string before it gets to the post_params method?
here's my models, view, and controller
Tag Model
class Tag < ActiveRecord::Base
has_and_belongs_to_many :post, join_table: 'tag_posts'
def self.createOrReturnExisting title
if Tag.any? {|tag| tag.title == title}
logger.debug "Tag: #{title} already exists"
Tag.find_by title: title
else
logger.debug "Tag: #{title} created"
Tag.new title: title
end
end
end
Post Model
class Post < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :tags, join_table: 'tags_posts'
has_one :type
validates :title, presence: true, length: { maximum: 50 }
validates :description, presence: true, length: { maximum: 255 }
validates :types_id, presence: true, length: { maximum: 255 }
end
new.html.erb
<h1>Post#new</h1>
<p>Find me in app/views/post/new.html.erb</p>
<%= form_for @post do |f| %>
Title: <%= f.text_field :title %>
Description: <%= f.text_area :description %>
Type: <%= f.collection_select( :types_id, Type.all, :id, :title ) %>
Content: <%= f.text_area :content%>
Tags: <%= f.text_area :tags%>
<%= f.submit %>
<% end %>
PostController
class PostsController < ApplicationController
load_and_authorize_resource
def miniList
render 'miniList'
end
def create
@post = Post.new
@post.title = params[:post][:title]
@post.description = params[:post][:description]
@post.content = params[:post][:content]
@tagStrings = params[:post][:tags].split(' ')
puts @tagStrings
@tagStrings.map do |name|
@tags << Tag.createOrReturnExisting name
end
@post.tags = @tags
@post.types_id = params[:post][:types_id]
if @post.save!
flash[:success] = "Post Saved Successfully"
else
flash[:error] = "Post not saved"
end
current_user.posts << @post
redirect_to :root
end
def new
@post = Post.new
render 'new'
end
def edit
end
def update
end
def delete
end
private
def post_params
params.require(:post).permit(:title,:description,:tags,:content,:types_id)
end
end
I figured it out, what I needed to do was change load_and_authorize_resource
to authorize_resource
since I don't want cancan messing with my parameters, this will just have it check authorization for my controller actions, and then leave the rest alone. I still wish there was a more intuitive way to do it, but this accomplishes what I need