Rails, form_for is updating attribute on all model records, not just the attribute belonging to the instance - form-for

I have created a view for volunteers to sign up for specific shifts. I am able to write my view and controller for shifts so that, when the form_for submit button is clicked, the current_user.id is pushed onto the specific shift's user_ids attribute, which is an array. The problem is that it updates each of the shift's user_ids with the current user's id. What am I missing here? Any insight would be appreciated. Thanks.
volunteer.html.erb
<div class="container">
<div style="width:60%;margin:0 auto 0 auto;" class="inner-lower">
<div class="list-group">
<% #uniq_shifts.each do |title| %>
<% #shifts_by_title = #shifts.where(title: title) %>
<% #title_vols = #shifts_by_title.pluck(:vols_needed).sum %>
<% #times = #shifts_by_title.pluck(:time) %>
<% #time_vols = #shifts_by_title.pluck(:vols_needed) %>
<% if #title_vols > 0 %>
<!-- ACTIVITY TITLE -->
<div id=<%= "activity#{title}" %> class="activity">
<a href="#" class="list-group-item">
<%= title %>
<!-- ACTIVITY NUMBER VOLUNTEERS NEEDED-->
<span class="badge-volunteer"><%= #title_vols %></span>
</a>
</div>
<div class="sub" style="display:none;">
<% #shifts_by_title.each do |shift| %>
<!-- ACTIVITY SHIFT -->
<a href="#" class="list-group-item-sub">
<!-- ACTIVITY SHIFT TIME -->
<%= shift.time %>
<span class="badge">
<!-- ACTIVITY SHIFT NUMBER OF VOLUNTEERS NEEDED -->
<%= shift.vols_needed %>
</span>
</a>
<%= form_for shift, :method => :put do |f| %>
<%= f.hidden_field :user_ids, :value => shift.add_user_id(#user.id) %>
<%= f.submit "sign up", class: "btn btn-primary" %>
<% end %>
<% end %>
</div>
<% end %>
<% end %>
</div>
</div>
shift.rb
class Shift < ActiveRecord::Base
has_and_belongs_to_many :users
def add_user_id(user_id)
user_ids_will_change!
update_attributes user_ids: self.user_ids + [ user_id ]
self.save
end
end
shifts.controller.rb
class ShiftsController < ApplicationController
before_action :set_shift, only: [:show, :edit, :update, :destroy]
before_action :volunteer, only: [:show, :edit, :update, :destroy]
def index
#shifts = Shift.all
end
def volunteer
#shifts = Shift.all
#user = current_user
#shift_titles = #shifts.pluck(:title)
#uniq_shifts = #shift_titles.uniq
#vols_needed = #shifts.pluck(:vols_needed)
unless current_user
render action: 'new'
end
end
def show
end
def new
#shift = Shift.new
end
def edit
end
def create
#shift = Shift.new(shift_params)
if #shift.save
redirect_to #shift, notice: 'Shift was successfully created.'
else
render :new
end
end
def update
#user = current_user
if #shift.update(shift_params)
redirect_to #shift, notice: 'Shift was successfully updated.'
else
render :edit
end
end
def destroy
#shift.destroy
redirect_to pages_url, notice: 'Shift was successfully destroyed.'
end
private
# Use callbacks to share common setup or contraints between actions.
def set_shift
#shift = Shift.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def shift_params
params.require(:shift).permit(:title, :time, :vols_needed, :user_ids => [])
end
end

Related

Standard Rails form + Cocoon gem: undefined method `new_record?' for nil:NilClass error

I'm following this tutorial and the author is using Slim. Since I more familiar with standard Rails form, I try to change the slim markup to normal form like so:
new.html.erb
<%= render 'form' %>
_form.html.erb
<%= form_for(#user) do |f| %>
<%= f.text_field :name %>
<br><br>
<%= fields_for :user_photos do |photo| %>
<%= render "user_photo_fields", f: photo %>
<span class="links">
<%= link_to_add_association "add photo", f, :user_photos %>
</span>
<% end %>
<%= f.submit %>
<% end %>
_user_photo_fields.html.erb
<div class="nested-fields">
<div class="field">
<%= f.file_field :photo %>
</div>
<%= link_to_remove_association "remove", f %>
</div>
And, this is my models:
class User < ActiveRecord::Base
has_many :user_photos
validates_presence_of :name
accepts_nested_attributes_for :user_photos, allow_destroy: true
end
class UserPhoto < ActiveRecord::Base
belongs_to :user
mount_uploader :photo, PhotoUploader
end
And lastly, strong params inside the users_controller.rb. I didn't touch the rest methods inside the controller because I'm using rails g scaffold user name:string generator.
def user_params
params.require(:user).permit(:name, user_photos_attributes: [:id, :photo, :_destroy])
end
I get this error:
undefined method `new_record?' for nil:NilClass
What am I missing here?
I believe it's just a simple typo - your fields_for :user_photos should be f.fields_for :user_photos (so that it's properly connected to the parent form).
please try with this.
class User < ActiveRecord::Base
has_many :user_photos
validates_presence_of :name
accepts_nested_attributes_for :user_photos, allow_destroy: true
end
can you try to fix this by removing the f
<div class="nested-fields">
<div class="field">
<%= file_field :photo %>
</div>
<%= link_to_remove_association "remove" %>
</div>

Rails 4.2 Creating Multiple New Records with One Form

I have three models: Lesson, Questions and Answers.
What I'm trying to do is on the show lesson view, display the questions and allow users to create answers for each answer. However, I'm not sure the best way to do this.
I tried this approach on my lesson#showview:
<% #questions.each do |question| %>
<%= question.content %><br /><br />
<%= simple_form_for :answers do |f| %>
<%= f.input :content %>
<%= f.hidden_field :question_id, :value => question.id %>
<%= f.button :submit %>
<% end %>
<% end %>
With this code, I receive the error param is missing or the value is empty: lesson
Answer has two fields: content, question_id.
My other concern is that I'd like to have this be user friendly, so if there are multiple questions, there should be multiple input boxes for the answers (one per each question) and one submit button (so multiple answers can be posted at one time).
I think that my approach my bad, but I'm not sure how else to do this, so any help would be greatly appreciated.
Here's what I have so far:
Models:
class Lesson < ActiveRecord::Base
has_many :questions, dependent: :destroy
has_many :answers, through: :questions
accepts_nested_attributes_for :questions, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :answers, reject_if: :all_blank, allow_destroy: true
end
class Question < ActiveRecord::Base
belongs_to :lesson
has_many :answers, dependent: :destroy
end
class Answer < ActiveRecord::Base
belongs_to :question
end
Lessons Controller
class LessonsController < ApplicationController
def show
#questions = #lesson.questions
end
# PATCH/PUT /lessons/1
# PATCH/PUT /lessons/1.json
def update
respond_to do |format|
if #lesson.update(lesson_params)
format.html { redirect_to #lesson, notice: 'Lesson was successfully updated.' }
format.json { render :show, status: :ok, location: #lesson }
else
format.html { render :edit }
format.json { render json: #lesson.errors, status: :unprocessable_entity }
end
end
end
private
def lesson_params
params.require(:lesson).permit(:name,
answers_attributes: [:id, :content, :question_id]
)
end
end
routes.rb
resources :lessons
post '/lessons/:id', to: "lessons#update"
Add gem in Gemfile and run bundle install:-
gem "nested_form"
On lession show page:-
<%= nested_form_for #lession do |lession_form| %>
<%= #lession.content %>
<%= lession_form.fields_for :questions do |question_form| %>
<% #questions.each do |question| %>
<%= question.content %><br /><br />
<%= question_form.fields_for :answers do |answer_form| %>
<%= answer_form.text_field :content %>
<%= answer_form.link_to_remove "Remove this answer" %>
<% end %>
<%= question_form.link_to_add "Add more answer", :answers %>
<% end %>
<% end %>
<%= lession_form.submit 'Update' %>
<% end %>
I would have thought you are able to achieve without the use of a Gem.
You may need to specify inverse_of in your model. I have previously found this was required in nested attributes when dealing with forms.
class Question < ActiveRecord::Base
belongs_to :lesson
has_many :answers, dependent: :destroy, :inverse_of => :question
end
class Lesson < ActiveRecord::Base
has_many :questions, dependent: :destroy, :inverse_of => :lessons
has_many :answers, through: :questions
#etc.
end
In your lessons controller:
def show
#lesson = Question.find(params[:id])
#questions = #lesson.questions
x.times { #lesson.questions.answer.build }
end
In your views/lessons/show page:
<%= form_for #lesson do |lesson| %>
<%= #lesson.whatever_attribute %>
<%= lesson.fields_for :questions do |question| %>
<% #questions.each do |question| %>
<%= question.content %>
<% end %>
<div id="answers-div" class='form-group'>
<%= question_form.fields_for :answers do |answer| %>
<%= answer.text_field :content id:"answer-entry" %>
<% end %>
</div>
<% end %>
<% end %>
<%= lesson.submit 'Submit' %>
Below the form add some buttons to add further answers or remove:
<button class="btn btn-default" id="addNewAnswer">Add Another Answer Box</button><br>
<button class="btn btn-default" id="deleteNewAnswer">Delete Last Answer</button>
You can then add & remove answers on the fly with jQuery.
$(document).ready(function(){
$("#addNewAnswer").click(function() {
$("#answers-div").append(createNewInputElement($("#answers-div")));
});
});
function createNewInputElement(form) {
var newIndex = $("#answers-div").children('input#choice-entry').length;
var newInput = $("#answer-entry").clone().attr('name', generateNewInputName(newIndex));
newInput.val('');
return newInput;
};
function generateNewInputName(idx) {
return "question[answers_attributes][" + idx + "][content]"
};
$(document).ready(function(){
$("#deleteNewAnswer").click(function() {
if ($("#answers-div input").length > 1) {
$("#answers-div input:last-child").remove();
}
});
});
The use of a nested form is not an issue. You require a nested form to allow you to nest answers within your lessson.questions but you are only allowing the user to give input
If using Rails 4 (and the Strong params) you will also need to allow these with something along these lines (otherwise the params being passed will not be allowed through).
private (in your Lessons controller)
def lesson_params
params.require(:lesson).permit(:content, answer_attributes:[:content])
end
This may not be perfect but it's the start of some sort of solution to your question I would hope.
Try to testing this code, an email me again
in your lesson#show.html.erb
<% for question in #lesson.questions %>
<%= question.content %><br /><br />
<%= simple_form_for :answers do |f| %>
<%= f.input :content %>
<%= f.hidden_field :question_id, :value => question.id %>
<%= f.button :submit %>
<% end %>
<% end %>

Validate that an end_date is later than a start_date in Rails 4

I am trying to determine the best way to validate that an end_date is greater than a start_date in a form.
I don't know how to throw an error if someone were to accidentally select a start_date that was greater than the end_date.
In my form, I have the ability to submit just a single date if the event was only one day (I use javascript to duplicate the start_date select values into the end_date select values), or a start_date and end_date if the event is longer than 24 hours.
The below scenario in the screenshot should trigger a 3rd error with the form.
This is the relevant part of the form:
event_form.html.erb
<%= form_for(#event, html: { multipart: true }) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="checkbox">
<label>
<%= f.check_box :single_day %> Is this a multi-day event?
</label>
</div>
<div class="field">
<%= f.label :start_date %><br />
<%= f.date_select :start_date, :start_year => Date.current.year, :end_year => 1900 %>
</div>
<div id="end_date_div" class="field">
<%= f.label :end_date %><br />
<%= f.date_select :end_date, :start_year => Date.current.year, :end_year => 1900 %>
</div>
<%= f.submit "Post", class: "btn btn-primary", :id=>"event_submit" %>
<% end %>
event.rb
class Event < ActiveRecord::Base
belongs_to :user
belongs_to :group
default_scope -> { order(created_at: :desc) }
mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :title, presence: true
validates :content, presence: true
validate :picture_size
private
# Validates the size of an uploaded picture.
def picture_size
if picture.size > 5.megabytes
errors.add(:picture, "should be less than 5MB")
end
end
end
events_controller.erb
class EventsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def create
#event = current_user.events.build(event_params)
if #event.save
flash[:success] = "Event created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
#event.destroy
flash[:success] = "Event deleted"
redirect_to request.referrer || root_url
end
private
def event_params
#may need to add the other variables here
params.require(:event).permit(:title, :content, :single_day, :start_date, :end_date, :picture)
end
def correct_user
#event = current_user.events.find_by(id: params[:id])
redirect_to root_url if #event.nil?
end
end
What you're looking for is another custom validation on your end_date field. You could write something in your model like the following:
validate :end_date_after_start_date?
def end_date_after_start_date?
if end_date < start_date
errors.add :end_date, "must be after start date"
end
end
This manually adds an error to your model, making sure validation does not succeed.
More information on custom validations:
http://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations

Determining if users are facebook friends (Parsing)

I'd like to find out if the current user is friends with the root node user, using Koala, and if so, print a statement saying: you are friends.
My problem is the parsing of data returned from facebook.
user.rb
def facebook
#facebook ||= Koala::Facebook::API.new(oauth_token)
block_given? ? yield(#facebook) : #facebook
rescue Koala::Facebook::APIError => e
logger.info e.to_s
nil # or consider a custom null object
end
def facebook_friend
facebook.get_connection("me", "friends/#user.user_id")
end
Profile.html.erb
<% if current_user.facebook_friend.include?(#user.uid)? %>
<p> you are friends</p>
<% else %>
<p> you are not friends</p>
<% end %>
Output:
<%= current_user.facebook_friend %>
will return: [{"name"=>"Elizabeth", "id"=>"100008217009369"}]
<%= #user.uid %>
will return: 100008217009369
Thanks!!!
It was much easier than I thought:
In the user model simple used the Koala method:
def facebook_friend(user)
facebook.get_connection("me", "friends/#{user.uid}")
end
def my_friends_list
facebook.get_connection("me", "friends")
end
Then in the Profile View, adding current_user and #user as arguments:
<% if #user.uid.present? %>
<% if signed_in? && current_user == #user %>
<p><%= current_user.my_friends_list.count %> friends</p>
<% elsif current_user.facebook_friend(#user).empty? %>
<p><%= current_user.mutual_friends.count %> friends in common</p>
<% else %>
<p>You are Facebook friends</p>
<% end %>
<% else %>
<p>Not connected</p>
<% end %>

Why isn't Template Toolkit aggregating my counter?

I'm working on a simple Dancer app to log books a person has read, but in my template to show how many books a person has read, I'm stumbling into an error. I'm trying to go through ever row in the table of reading instances and add 1 to a counter if the reader is the same as the listed person.
Here's the code for the template:
<ul class="people">
<% IF people.size %>
<% FOREACH id IN people.keys.nsort %>
<li><h2 style="display: inline;"><% people.$id.name %></h2><br />
Born <% people.$id.birthday %><br />
<% FOREACH reader IN readings.keys.nsort %>
<% count = 0 %>
<% IF readings.$reader.person_id == people.$id.id %>
<% count = count + 1 %>
<% END %>
<% END %>
<% count %>
<% END %>
<% ELSE %>
<li><em>Unbelievable. No people here so far</em>
<% END %>
</ul>
However, when I display it, count is only 1. Does anybody know what I'm doing wrong, or do you need more code?
Thanks.
Looks like you need to pull the count initialization out of the FOREACH reader loop:
<% FOREACH id IN people.keys.nsort %>
<li><h2 style="display: inline;"><% people.$id.name %></h2><br />
Born <% people.$id.birthday %><br />
<% count = 0 %>
<% FOREACH reader IN readings.keys.nsort %>
<% IF readings.$reader.person_id == people.$id.id %>
<% count = count + 1 %>
<% END %>
<% END %>
<% count %>
<% END %>