Adding an object to an array of favorite objects - ruby-on-rails

I'm working on a site which lists many events and I want users who are logged in to be able to add an event to a personal list of favorite/watched events.
I have
event.rb
has_and_belongs_to_many :profiles
and
profile.rb
has_and_belongs_to_many :events
If a user is logged in, I want to a button on each event view that adds that event to the current_user's profile. Basically, this functionality:
current_user.profile.events << event
It works to do that in the console but I can't figure out how to create a form which does this. From this looking at various answers on this website, this is what I have:
Routes.rb
map.resources :events, :member => { :create_calendar => :post }
_event.html.erb
<%= form_for( current_user.profile.events(event) ) do |f| %>
<%= f.submit "Add to Calendar" %>
<% end %>
events_controller.rb
def create_calendar
#event = Event.find(params[:event_id])
current_user.profile.events << #event
end
Any guidance, advice, or tips would be GREATLY appreciated!

I'm not a fan of has_and_belongs_to_many so my advice would be to ditch it and make a new model. Maybe something like this
class SavedEvent < ActiveRecord::Base
belongs_to :user
belongs_to :event
end
Then you can do something like this:
Events View:
form_for SavedEvent.new, :url => "/events/save_event" do |f|
f.hidden_field :event_id, :value => #event.id
f.submit "Save Event"
end
Then to save it:
def save_event
#saved_event = SavedEvent.new(params[:saved_event]
#saved_event.user = current_user
#saved_event.save
end
Naturally you can trim this down a bit and make it a bit more RESTful, but that would be the main building block of how I'd go about doing it.

Related

Rate a post only once per user in rails

I have trouble in allowing users to rate a post. My task is to enable the user to rate a post only once. On the show page, the post I have includes radio buttons for rating. If the user tries to rate for the second time it needs to update the previous rating done by the user for the same post. The issue I am facing is that user is able to rate a post multiple times. How to resolve this?
User model:
class User < ApplicationRecord
has_many :posts
has_many :ratings
end
Post model:
class Post < ApplicationRecord
has_many :ratings
belongs_to :user
end
Ratings model
class Rating < ApplicationRecord
belongs_to :post
belongs_to :user
end
In the post controllers i have used nested attributues for ratings.
def show
#post = #topic.posts.find(params[:id])
#rate = #post.ratings.all
#rate = Rating.where(post_id: #post.id).group("rate").count
end
private def post_params
params.require(:post).permit(:title, :body, ratings_attributes: [:rate])
end
The show page of post include the creating of rating using <fieldset>:
<%= form_for [#topic, #post] do |f| %>
<%= f.fields_for :ratings, #post.ratings.build do |builder| %>
<fieldset>
<% for i in 1..5 %>
<%= builder.radio_button :rate, i %><%= i %>
<% end %>
</fieldset>
<% end %>
<%=f.submit "Rate" %>
<% end %>
First, add validation to the Rating to enforce uniqueness on the combination of user and post. This will stop a duplicate rating ever being created.
validates_uniqueness_of :post_id, scope: :user_id
Then, in the action that saves the rating, first check if there is a record that can be updated, else create a new one.
#rating = Rating.find_or_initialize_by(user: #user, post: #post)
#rating.rate = params[:rate]
#rating.save
This might not be perfect syntax, but you should get the idea about what you are trying to do and can adjust to match your code.
You can use first or initialize like this
#rating=Rating.where(post_id: #post.id,user_id: current_user).first_or_initialize

Rails wizard form

I have an enrollment form where a user can enroll to some sort of event.
However, I want to give the posibility for teams to enroll also and I was thinking about a wizard like form.
Basically create 5 records at a time.
The problem is, I'll have a new enrollment creation on each step, so I thought the wicked gem would not do it for this scenario.
Can you give me a few guidelines on how should I approach this?
Maybe just render new after creation if a i.e. team attr is sent from the form?
Maybe use self join?
That's off the top of my head but I know there has to be a clever way to do this.
I'm not sure how your models are structured, but if you have something like:
class Attendee
has_many :enrolments
has_many :events, through: :enrolments
end
class Enrolment
has_many :attendees
belongs_to :event
end
class Event
has_many :enrolments
has_many :attendees, through: :enrolments
accepts_nested_attributes_for :enrolments
end
Then you can do something like:
# controllers/enrolments_controller.rb
class EnrolmentController < ApplicationController
def new
#event = Event.find(params[:event_id])
pax = params[:persons].to_i
pax.times do
#event.enrolments.build
end
end
def create
#event = Event.find(params[:event_id])
#event.enrolments.build(enrolment_params)
#event.save
end
protected
def enrolment_params
# require specific parameters here
params.require(:event).permit(:attendee_attributes => [])
end
end
# views/enrolments/new.html.erb
<%= form_for #event, url: event_enrolments_path(#event) do |f| %>
<%= f.hidden_field :event_id %>
<%= f.fields_for :enrolments do |af| %>
<%= af.select :attendee_id, Attendee.all.collect {|p| [ p.name, p.id ] } %>
<% end %>
<%= f.submit %>
<% end %>
# routes.rb
resources :events do
resources :enrolments
end
That's off the top of my head, but the general idea is that you build the nested fields by running event.enrolments.build based on the number of people passed in the params.
This uses fields_for and accepts_nested_attributes_for. This also makes it really convenient to reuse existing forms by passing in the form context in the partial:
<%= f.fields_for :enrolments do |af| %>
<%= render "enrolments/form", f: af %>
<% end %>

Nested Models with Comments

I have an application where there are a few nested models... Two parent models and two child models.
I'm trying to create comments on the child models and I had it working great for the first one until I realized I have to create comments on the second child so I realized I had to scrap my work because I was targeting the first parent + child model in the comments controller. So I decided to watch Ryan Bates screencast (http://railscasts.com/episodes/154-polymorphic-association) on creating comments that belong to multiple models...unfortunately, it's not working for me and I'm assuming its because I am trying to create comments on the child models. I will show you what I was using before that worked for the one model and I'll show you what im doing now that doesnt work...
here is what i had for the comments controller
def create
#collection = Collection.find(params[:collection_id])
#design = #collection.designs.find(params[:design_id])
#comment = #design.comments.create(comment_params)
#comment.user = current_user
#comment.save
redirect_to collection_design_path(#collection, #design)
end
and here is what it is now after i tried to implement it to work for multiple models
def create
#commentable = find_commentable
#comment = #commentable.comments.build(comment_params)
#comment.user = current_user
#comment.save
end
private
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
here are my crazy routes
resources :collections do
member do
post :like
post :unlike
end
resources :designs do
resources :comments
member do
post :like
post :unlike
end
end
end
anyone have any other different ideas for created comments for multiple nested models? Thanks in advance for the help.
EDIT:
Here was the form I was using for the one model
<%= form_for([#collection, #design, #design.comments.build]) do |f| %>
<%= f.text_area :comment %>
<%= f.submit "Comment", :class => "btn" %>
<% end %>
and here is the one i'm using now
<%= form_for([#collection, #design, #commentable, Comment.new]) do |f| %>
<%= f.text_area :comment %>
<%= f.submit "Comment", :class => "btn" %>
<% end %>
Right now when I try to submit the new comment form I get this error
undefined method `comments' for #<Collection:0x0000010150cf88>
which points back to the create method
EDIT 2
Here is my comment model
belongs_to :commentable, :polymorphic => true
belongs_to :user
Here is my design model (which is a child of the collection model)
has_many :comments, :dependent => :destroy, :as => :commentable
belongs_to :user
belongs_to :collection
and my collection model (which has the child model: design)
belongs_to :user
has_many :designs, :dependent => :destroy
and there is more to the models but its not related to the problem.

rails - update_attributes for just part of the model; file uploading

I want to add the ability of a user to have several pictures associated with his / her user account.
I have the following classes:
class User < ActiveRecord::Base
has_many :assets
accepts_nested_attributes_for :assets
end
class Asset < ActiveRecord::Base
belongs_to :assetable, :polymorphic => true
belongs_to :user
end
I want to have a screen that just has the upload image functionality:
def add_profile_picture
#user=User.find(params[:id])
1.times {#user.assets.build}
end
form:
<%= form_for #user do |u| %>
<%= u.fields_for :assets do |asset| %>
<%= asset.file_field :asset %>
<%= asset.text_field :description %><br />
<% end %>
<%=u.submit %>
<% end %>
When I submit, it looks like the id value goes in ok in development.log:
"id"=>"1"
but I get the error:
undefined method `update_attributes' for nil:NilClass
Since I just have the asset fields, is there anything special I need to do? Also, because the belongs_to :user exists, could that be causing problems?
Basically:
asset:
user_id:
assetable_type:
assetable_id:
Any help would be appreciated. Don't do much Rails forms stuff.
thx
edit #1
class UsersController < ApplicationController
def add_profile_picture
#user=User.find(params[:id])
1.times {#user.assets.build}
end
thx
Okay - there are a few problems with your code here. I would highly recommend you read both the Action Controller Overview and the Rails Routing guides to get some more information about this.
In any case, you're getting the error because the form you have there will be trying to use the users#update action in the UsersController.
You've got a couple options. One is to create the necessary routes for the custom action, or you can create a nested resource, and make a form for adding the asset.
In this case, you'd do something like this:
in routes.rb
resources :users do
resources :assets, :only => [:new, :create] # Or any other actions you might want. It's best practise to limit these.
end
Then, in the AssetsController, you can do something similar to this:
def new
#asset = Asset.new
end
def create
#asset = Asset.new(params[:asset])
#asset.user_id = params[:user_id] if params[:user_id]
#asset.save!
end
and your form will look something like this:
<%= form_for #asset do |f| %>
<%= f.file_field :asset %>
<%= f.text_field :description %><br />
<%=f.submit %>
<% end %>

Associating notes with different entities in a database

At my job, we have judges perform a variety of tasks, e.g., rate movies or compare two pieces of text.
We're in the process of designing a new database to hold all our data (we have some data already, but the database it's in is pretty hack), and I'm starting to build a Rails analytics application that will serve as a dashboard on these judgments. Tables will include things like Judges, Movies, Text, MovieRatings, TextComparisons.
As part of the application, we want to be able to add comments or flag items from these tables. For example, someone might want to add a comment to Judge 1 saying "This judge is very inconsistent" and add a comment to Rating 2 saying "This rating is unexpected", or flag different types of movies or texts for review.
What is the best way to handle adding comments or flags to the database? For example, do we want to create a new Comment table for each entity (add a JudgesComments, MoviesComments, TextComments, etc.)? Or do we want to have a single Comments table with (id, comment) columns [which, I guess, would require ids throughout the database to be globally unique within the database, instead of unique only within its table]?
You should use polymorphic associations, so that you will have a single Comment model and controller. According to the excellent #154 "Polymorphic Association" Railscast, after adding a commentable_type:string and commentable_id:integer to your comments table, your code should look something like this:
# app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
# app/models/judge.rb
class Judge < ActiveRecord::Base
has_many :comments, :as => :commentable
end
# app/models/movie.rb
class Movie < ActiveRecord::Base
has_many :comments, :as => :commentable
end
# app/models/text.rb
class Text < ActiveRecord::Base
has_many :comments, :as => :commentable
end
# app/controllers/comments_controller.rb
def index
#commentable = find_commentable
#comments = #commentable.comments
end
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
if #comment.save
flash[:notice] = "Successfully created comment."
redirect_to :id => nil
else
render :action => 'new'
end
end
private
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
in the routes:
# config/routes.rb
map.resources :judges, :has_many => :comments
map.resources :movies, :has_many => :comments
map.resources :texts, :has_many => :comments
and in the view:
<!-- app/views/comments/index.html.erb -->
<div id="comments">
<% for comment in #comments %>
<div class="comment">
<%=simple_format comment.content %>
</div>
<% end %>
</div>
<h2>New Comment</h2>
<%= form_for [#commentable, Comment.new] do |f| %>
<p>
<%= f.label :content %><br />
<%= f.text_area :content %>
</p>
<p><%= f.submit "Submit" %></p>
<% end %>

			
				
I have worked in a system where there was a single Comments table, and a globally unique ID for each record in each table you could have a comment for. The system worked fine, wasn't hard to maintain, and it was easy for new people to see how it worked. Generating new records in the comment-able tables was slow by computer standards, but it wasn't really an issue for the users of the system.

Resources