boolean field not updating in Rails - ruby-on-rails

I am building a basic crud app; with my controller method for updating a task as follows :-
def update
#task = Task.find(params[:id])
respond_to do |format|
if #task.update(task_params)
format.html { redirect_to task_url(#task), notice: "Task was successfully updated." }
format.json { render :show, status: :ok, location: #task }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
Tasks have a boolean field completed.
create_table "tasks", force: :cascade do |t|
t.string "title"
t.text "content"
t.string "createdby"
t.datetime "createdat", precision: 6
t.boolean "completed"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
Issue is when I am trying to update tasks, all fields apart from completed gets updated.
Updating completed to false gives me Task updated message successfully, but value of the boolean field reverts back to true no matter how many times I change it.
P.S. I am passing in field completed as params.
Below is the view for rendering the checkbox for boolean field completed :-
<div class="form field-group">
<%= form.check_box :completed, { class: "class-name", style: "style"}, "checked-value", "unchecked-value" %> Completed
</div>
I am totally new to ROR & any help would be appreciated.
Tried updating task from console as follows :-
=> Task.last.update(:completed => false).save
Does update, but reverts back to true everytime update is done from browser ui

Your values for the checkbox appear to be "checked-value", "unchecked-value" so when it is checked it returns a string of either "checked-value" or "unchecked-value" Since they are both strings they evaluate to true. Change them to true and false.
<%= form.check_box :completed, { class: "class-name", style: "style"}, "true", "false" %> Completed

<%= form.check_box :completed, { class: "class-name", style: "style"}, "checked-value", "unchecked-value" %>
When the check box is not ticked your server will receive "unchecked-value" as the value for completed. This will still evaluate to true. You can try this by doing the following in the console:
> task = Task.new
> task.completed = "unchecked-value"
> puts task.completed
true
The default unchecked value is "0" which will give the expected behaviour.
> task.completed = "0"
> puts task.completed
false
Unless you have a reason for having "unchecked-value" a fix would be just to use the defaults:
<%= form.check_box :completed, { class: "class-name", style: "style"} %>

Related

Rails create does not save params from checkbox

I am creating a website where a user (interviewer) can create a position (a job opening).
Even if the params are sent, my create action does not save them except for the current_user.
This is what I send:
positions_controller.rb
def new
#position = Position.new
end
def create
#position = Position.new(position_params)
#position.interviewer = current_interviewer
if #position.save
redirect_to position_path(#position)
flash[:success] = "You created a new position/opening"
else
render :new
end
raise
end
private
def set_position
#position = Position.find(params[:id])
end
def position_params
params.require(:position).permit(:title, :skills, :interviewer)
end
end
_form.html.erb
<%= simple_form_for [#interviewer, #position] do |f| %>
<%= f.input :title, required:true %>
<%= f.input :skills, as: :check_boxes, collection:[
['Python', "python"],
['Java', "java"],
['JavaScript', "javascript"],
['Ruby', "ruby"],
['C++', "c++"],
['Node.js', "node"],
['React', "react"],
['Django', "django"],
['Rails', "rails"],
['SQL', "sql"],
['Doker', "doker"],
['AWS', "aws"],
['Vue.js', "vue"],
['Marketing', "Marketing"],
['HR', "hr"],
['Finance', "finance"],
['IT', "it"],
], input_html: { multiple: true } %>
<%= f.submit "Submit position", class: "btn btn-primary" %>
<% end %>
position.rb
class Position < ApplicationRecord
validates :title, presence: true
belongs_to :interviewer
end
schema
create_table "positions", force: :cascade do |t|
t.string "title"
t.bigint "interviewer_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "candidate_id"
t.string "candidatures", default: [], array: true
t.string "skills"
t.index ["candidate_id"], name: "index_positions_on_candidate_id"
t.index ["interviewer_id"], name: "index_positions_on_interviewer_id"
end
My alternative was to replace the create code with:
#position = current_interviewer.positions.new(position_params)
but it still does not work.
Since you have a input_html: { multiple: true } for the params skills, you need to add the following in positions_controller.rb:
def position_params
params.require(:position).permit(:title, :interviewer, :candidate, skills:[])
end
Basically, your skills will be saved as an array if you allow input_html: { multiple: true } for a collection
In addition, you are not passing any params for candidate
In your table, you have interviewer_id but in your permitted params you have interviewer.
Change your params to permit interviewer_id instead.
Also, in your form you have <%= f.input :title, required:true %> Presence is required by default (at least with simple_form). You don't need to declare it in the form, but you should still keep it in your Model.

RoR - undefined method `draft?' for nil:NilClass

Actually I am trying to use enum attribute in my rails 5 blog app.The problem is that when I try to toggle the status from draft to published or vice versa, I get the error stating "NoMethodError in BlogsController#toggle_status"
blogs_controller.rb
class BlogsController < InheritedResources::Base
def toggle_status
if #blog.draft?
#blog.published!
elsif #blog.published?
#blog.draft!
end
redirect_to blogs_url, notice: "Blog status has been updated"
end
private
def blog_params
params.require(:blog).permit(:title, :body)
end
end
index.html.slim
h1 Listing blogs
table
thead
tr
th Title
th Body
th
th
th
th
tbody
- #blogs.each do |blog|
tr
td = blog.title
td = blog.body
td = link_to blog.status,toggle_status_blog_path(blog)
td = link_to 'Show', blog
td = link_to 'Edit', edit_blog_path(blog)
td = link_to 'Destroy', blog, data: { confirm: 'Are you sure?' }, method: :delete
br
= link_to 'New Blog', new_blog_path
blog.rb
class Blog < ApplicationRecord
enum status: { draft: 0, published: 1 }
end
routes.rb
Rails.application.routes.draw do
resources :blogs do
member do
get :toggle_status
end
end
end
schema.rb
create_table "blogs", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "status", default: 0
end
I don't know where I may be going wrong, I tried at my best but unable to figure it out.
Any suggestions are most welcome.
Thank you in advance.
You'll need to set your #blog instance variable.
def toggle_status
#blog = Blog.find(params[:id])
...

How to use Range as one of parameters for POST Data to rails controller

The database migration has experience field defined as a range
class CreateJobPosts < ActiveRecord::Migration[5.1]
def change
create_table :job_posts do |t|
t.string :name, null: false, limit: 100
t.string :location, null: false, limit: 100
t.int4range :experience, null: false
t.text :description, null: false
t.text :skills, null:false
t.boolean :active, null: false, default: false
t.string :seo_meta_keywords, array: true, null: false
t.string :seo_meta_description, null: false, limit: 150
t.timestamps
end
add_index :job_posts, :name, unique: true
add_index :job_posts, :location
add_index :job_posts, :active
end
end
Now while writing tests, I have FactoryGirl defined some models like so
FactoryGirl.define do
factory :job_post do
name "DevOps Coach"
location "Bangalore"
experience (4..10)
description 'Test Description'
skills 'Test Skills'
active true
seo_meta_keywords ['keywords','keywords']
seo_meta_description 'dummy descriptions'
end
end
And in the controller
def job_post_params
params.require(:job_post).permit(:name, :location, :experience, :description, :skills, :active, :seo_meta_description, seo_meta_keywords: [])
end
The attribute hashes are initialised as expected
[89, 98] in /Users/anadi/Code/bauji/spec/controllers/job_posts_controller_spec.rb
89:
90: context "with invalid params" do
91: it "returns a success response (i.e. to display the 'new' template)" do
92: byebug
93: temp = invalid_attributes
=> 94: post :create, params: {job_post: invalid_attributes}, session: valid_session
95: expect(response).to be_success
96: end
97: end
98: end
(byebug) temp
{:name=>"No Name", :location=>"Nay", :experience=>4..10, :description=>"Test Description", :skills=>"Test skills", :active=>true, :seo_meta_keywords=>["keywords", "keywords"], :seo_meta_description=>"dummy descriptions"}
but the POST & PUT method tests fail because :experience attribute is nil for controller
[24, 33] in /Users/anadi/Code/bauji/app/controllers/job_posts_controller.rb
24: # POST /job_posts
25: # POST /job_posts.json
26: def create
27: byebug
28: #job_post = JobPost.new(job_post_params)
=> 29: respond_to do |format|
30: if #job_post.save
31: format.html { redirect_to #job_post, notice: 'Job post was successfully created.' }
32: format.json { render :show, status: :created, location: #job_post }
33: else
(byebug) #job_post
#<JobPost:0x007fda5fb21920>
(byebug) #job_post.experience
*** ArgumentError Exception: bad value for range
nil
Solution:
# Never trust parameters from the scary internet, only allow the white list through.
def job_post_params
raw_post_params = params.require(:job_post).permit(:name, :location, :experience, :description, :skills, :active, :seo_meta_description, seo_meta_keywords: [])
range_begin, range_end = raw_post_params[:experience].split('..').map { |v| Integer(v) }
raw_post_params[:experience] = Range.new(range_begin, range_end)
raw_post_params
end
Could it be more compact?
When you make the POST request, the 4..10 is most likely converted to a String: "4..10". Try and parse the string in your controller with something similar to:
range_begin, range_end = params[:experience].split('..').map { |v| Integer(v) }
experience = Range.new(range_begin, range_end)
Then you can set that experience to attribute on your JobPost

How to create a method to calculate minutes per mile in Ruby on Rails

In my Rails 4.2.6 / Ruby 2.2.4 project, I have a cardio exercise model with duration, distance and average pace fields. "Duration" and "average pace" are time datatypes; "distance" is a decimal datatype. I need help writing a method to calculate a user's average pace (minutes per mile) based on a user's inputs in the duration and distance fields.
* EDIT *
The duration and distance fields in my cardio exercises form (_form.html.erb):
<div class="cardio_duration">
<%= f.input :duration do %> <br>
<%= f.time_select :duration, :include_blank => true, include_seconds: true %>
<% end %>
</div>
<div class="cardio-time">
<%= f.input :distance, :include_blank => true %>
<% end %>
</div>
Here's what I have coded so far in my cardio exercise model:
before_save { self.average_pace = self.calculate_average_pace } # calls calculate average pace method
I understand that when calculating minutes per mile, the time factor is on top in the equation. Here's the method I've written:
def calculate_average_pace
duration = self.duration
distance = self.distance
average_pace = duration / distance
end
When I execute this method, it generates the following error:
undefined method `/' for 2000-01-01 16:50:00 UTC:Time
I'm trying to do something like this: 26 minutes / 1.17 miles, which equals 22.29 minutes per mile. How should I change my code to fix the problem? Thanks for the help & explanation.
**EDIT **
This is the relevant DB schema:
create_table "cardio_exercises", force: :cascade do |t|
...
t.datetime "created_at"
t.datetime "updated_at"
t.time "duration"
t.decimal "distance"
t.time "average_pace"
end
You're getting undefined_method error because you're trying to divide Time by Decimal. Only Numeric values can be divided.
I tried to achive that functionality like this.
# _form.html
# ...
<div class="field">
<%= f.label :duration %><br>
<%= f.time_select :duration, include_blank: false, include_seconds: true %>
</div>
# ...
# Cardio model
class Cardio < ActiveRecord::Base
before_save :set_mpm
def set_mpm
# find minutes from time entered by user
mins = duration.hour * 60 + duration.min + duration.sec / 60
self.mpm = mins / distance
end
end
# controller
def create
#cardio = Cardio.new(cardio_params)
respond_to do |format|
if #cardio.save
format.html { redirect_to #cardio, notice: 'Cardio was successfully created.' }
format.json { render action: 'show', status: :created, location: #cardio }
else
format.html { render action: 'new' }
format.json { render json: #cardio.errors, status: :unprocessable_entity }
end
end
end
Here is the result
Is that works for you?
UPDATE
1 hour for 10 miles works for me.
Here is my DB schema.
create_table "cardios", force: :cascade do |t|
t.time "duration"
t.decimal "distance"
t.decimal "mpm"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

Append a string to an array through strong parameters using << or Find_By_Sql in Rails

I am trying to append a string value to two array columns ("good_product" & "bad_product") of a Postgresql model ("product.rb") using strong parameters with user input from form_for text fields. I have found two ways (<< & find_by_sql) to achieve this goal but can't get either to work.
For example, "Product 3" submitted through form_for input should be added to {"Product 1", "Product 2", _______ } (if other products exist in the array) OR an empty array (if no product added yet).
Model: product.rb
class Product < ActiveRecord::Base
serialize :good_product, Array
serialize :bad_product, Array
end
Controller: products.controller.rb
def new
#product = Product.new
end
def create
#product = Product.new(product_params)
#product.good_product ||= []
#product.bad_product ||= []
#product.good_product << :good_product
#product.bad_product << :bad_product
redirect_to '/products'
end
private
def product_params
params.require(:product).permit(:good_product => [], :bad_product => [])
end
end
View: new.html.erb
<%= form_for #product, url: products_path do |f| %>
<%= f.label :good_product, "Good Product" %>
<%= f.text_field :good_product %>
<%= f.label :bad_product, "Bad Product" %>
<%= f.text_field :bad_product %>
<%= f.submit 'Submit' %>
<% end %>
Schema: schema.rb
create_table "products", force: :cascade do |t|
t.string "good_product", default: [], array: true
t.string "bad_product", default: [], array: true
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "products", ["user_id"], name: "index_products_on_user_id", using: :btree
Alternatively, instead of "<<", I tried this in the Controller:
def create
#product = Product.new(product_params)
Product.find_by_sql(["UPDATE products SET good_product = array_append(good_product, '?') WHERE user_id = ?", #product.good_product, current_user]);
Product.find_by_sql(["UPDATE products SET bad_product = array_append(bad_product, '?') WHERE user_id = ?", #product.bad_product, current_user]);
redirect_to '/products'
end
Thank you all for your help!

Resources