How to append an array in parameters after Rails form submit? - ruby-on-rails

I have a form with checkboxes that get passed as an array "list_person_ids" on form submit. My models are "Occurance" which has an n:m relationship with "ListPerson" through the Model "Person". "list_person_ids" are saved in Person with the Occurance id and the ListPerson id.
I want to append one or more values to the array before this gets saved. The reason I need to do this is because the user can also add a new value in ListPerson using a textbox with the name "person_name".
def create
#occurance = Occurance.new(occurance_params)
add_person_id(#occurance)
...
# save
end
def add_person_id(object)
if params[:person_check] == '1'
object.list_person_ids.push( ListPerson.find_or_create_by(person: params[:person_name]).id )
end
end
def occurance_params
params.require(:occurance).permit(:person_check, :person_name, dim_person_ids: [])
end
find_or_create_by is successful, but nothing gets pushed to the array "list_person_ids". There is also no error message. Do I have to give permission somewhere to append this array? Please let me know if some information is missing.

on your model you can do something like below:
....
before_create :create_lists
private
def create_lists
ListPerson.find_or_create_by(list_person_ids: person_name.id)
end

Related

rails mongoid undefined bson_type

I defined my own custom field type Price for my application. I defined all the required methods (demongoize, mongoize, ..) and it works perfectly fine retrieving the data from mongodb, sending it to front end and getting data back and store them in mongo db. My Price class stores the data as an array [value, currency] in the DB and also my front end expect the price to be delivered as a an array. All ok so far.
In my model I now added a Hash which reflects a tree structure containing objects which have a price. I was re-using the price class for these prices when creating the tree structure.
But now I get an undefined bs_type error when I try to save this hash field to the database. Looks like rails/mongoid doesnt find a method to transform the price in the Hash into a suitable mongoid format.
I tried to define as_json, to_json, to_s, ... everything on my Price model but I always get the same message: undefined bson_type for Price whenever I want to save my tree field.
(If I store my values as [value,currency] in the hash - not using Price, obviously all works fine)
Any idea?
class Price
def initialize(value = 0, currency = '')
#value,#currency = value, currency
end
def mongoize
[#value,#currency.to_s]
end
class << self
def demongoize(object)
if object
cur = object[1] || ''
Price.new(object[0], cur)
else
Price.new()
end
end
def mongoize(object)
case object
when Price then object.mongoize
when Array then Price.new(object[0], object[1]).mongoize
else object
end
end
def evolve(object)
case object
when Price then object.mongoize
else object
end
end
end
I have another model:
class Financial
include Mongoid::Document
field: quote, type: Price
field: tree, type: Hash
end
In my application code setting the quote and saving it works.
Here is the problem:
revenue = Price.new(10,'USD')
record = Financial.new()
record.quote = revenue
record.save # All works so far
record.tree = { data: [10,'USD']}
record.save # this works too
record.tree = { data: revenue}
record.save # here I get the undefined bson_type for Price error.

How can I build out child associations on my object in Rails without saving the parent?

I'm trying to initialize an object, but I don't want to create the object to the database until the user has click saved. I've gotten this to work with the parent object only, but I can't seem to get my child objects to associate to the parent because I don't have an id yet for the parent object. What am I doing wrong in the code below? Thanks.
def new
# Build a new report object with the default type of "draft" and assign it to the player
report_options = { player_id: #player.id,
author_id: current_user.id,
position: #player.position,
type: "draft",
submitted: false }
#report = Report.new(report_options)
#skills.where('disabled = (?)',FALSE).each do |skill|
# On the line below I get an evaluation record with a proper skill id,
# but report_id is `nil` because I'm guessing the report hasn't been
# created yet. Is there a way to reserve an id for it without actually
# committing the record to the database until the user saves the record?
#report.evaluations.build(skill_id: skill.id)
end
end
The model of evaluations is not quite clear to me, but basically if you do something like
#report.evaluations.build
#skills.where('disabled = (?)', FALSE).each do |skill|
#report.evaluations << Evaluation.new(skill_id: skill.id)
end
it will add objects to evaluations without saving and won't require the report.id to exist at the moment of adding.

Build array of objects from params hash after Transaction block fails, Rails

I have a form where a user can update multiple resources at the same time. The transaction block makes the form atomic: if one validation fails for any of the resources being updated, then none of the resources get updated, and active record rollsback all changes.
When transaction fails, I want to render the form again, display the same input that the user entered along with errors next to each of the error input fields which prevented the transaction from going through.
The transaction block works. What I am having trouble with is building the array of objects from the params log. Each index of the array should contain a hash which holds key/value pairs of all the attributes of a specific resource.
UDPATE: BELOW IS THE ANSWER CODE THANKS TO THE RESPONSES
Code:
def update_multiple
begin
User.transaction do
params[:users].each do |k, v|
User.find(k).update!(v)
end
flash[:notice] = "Update Successful"
redirect_to :users and return
end
rescue
#users = []
params[:users].each do |k,v|
#users.push(User.new({:id => k}.merge(v)))
end
flash[:error] = "Errors found"
render :edit_multiple and return
end
end
And for good measure, here is what the passed in parameters looks like in the log. This transaction fails because the name attribute must be at least 3 characters long.
Parameters: {"utf8"=>"✓", "authenticity_token"=>"xyz=", "users"=>{"15"=>
{"name"=>"Neil", "age"=>"11"}, "16"=>{"name"=>"z", "age"=>"33"}, "17"=>
{"name"=>"John", "age"=>"99"}}, "commit"=>"Submit Changes"}
Thanks in advance! Any way to write this code better in ruby is much appreciated as well.
Ok, so you're trying to iterate through a list of objects in your params using a for_each and an external iterator, you really don't want to do that. I'd suggest something like this:
params[:users].each do |k,v|
# k is the "key" of each user while v is the values associated with each key
#users.push(User.new(:id => k, v)
# I'm doing this in my head so you might need:
# #users.push(User.new({:id => k}.merge(v))
# if you start getting errors about looking for a proper hash or something
# I can't remember how good Rails/Ruby is at recognizing nested hashes
end
That should produce a new user for each user object passed in using the ID provided and the values associated with each value.

How do I iterate through a hash in an objects param hash?

Rails (and coding) rookie here (I'm sure I'm just missing basic syntax structural stuff), I've created a form where the user can add as many field pairs as they like via AJAX (barely). This form will collect column titles for a 'sheet' and the associated data type (int, str... etc). The sheet will have item entries added later by users. I'm trying to make a Sheets controller create method that not only saves the title and description of the sheet, but also adds a record to the 'columns' table with the column title, data type and associated sheet id. When I submit the sheet form, I get the following params in the server terminal:
(sorry I'm not sure how to wrap the code snippet)
{"utf8"=>"✓", "authenticity_token"=>"yMlnfO1EWptkEXp5+9AGCO5C3vHt62EUHoKjdWoUB8I=", "sheet"=>{"title"=>"test 33", "description"=>"Descriptions"}, "column"=>[{"title"=>"1", "type"=>"num"}, {"title"=>"2", "type"=>"int"}, {"title"=>"3", "type"=>"real"}, {"title"=>"fo", "type"=>"no"}], "commit"=>"Save Specsheet!"}
I'm trying to loop through the column hash to create a record on the columns table. Each hash would use the title and type values as entries on the table.
My create method:
def create
#sheet = Sheet.new(sheet_params)
#sheet[:column].each do |key, value|
#column = Column.new
#column[:column_title] = key
#column[:column_data_type] = value
#column.save
end
if #sheet.save
redirect_to #sheet
else
flash[:error] = "Error saving sheet."
render :new
end
end
My error is usually something like this:
undefined method `each' for nil:NilClass
**#sheet[:column].each do |key, value|**
#column = Column.new
#column[:column_title] = key
#column[:column_data_type] = value
So I know I'm messing up referencing the column hash and its key and values. I'm thinking I can .reduce something here? I have no idea. These kinds of basic structural questions don’t really show up with googling, so please let me know what I'm doing wrong, and thank you for reading all of this! Cheers!
WORKING CODE (sorry for weird formatting)
def create
#sheet = Sheet.new(sheet_params)
column_params.each do |value|
#sheet.columns.build(value.permit(:title, :data_type))
end
if #sheet.save
redirect_to #sheet
else
flash[:error] = "Error saving sheet."
render :new
end
end
private
def sheet_params
params.require(:sheet).permit(:title, :description, :created_at, :updated_at, :column)
end
def column_params
params.require(:column)
end
When you are calling #sheet[:column], you are referencing an instance of Sheet instead of the params that you are trying to cycle through.
If you are trying to associate Columns to Sheets in a has_many relationship, you can create Columns like:
column.each do |key, value|
#sheet.columns.new(
column_title: key
column_data_type: value
end
end
(and then save block)
in your controller. Where column are the params. This will indicate that the column belongs to the sheet instance.
If you are trying to make the Column record without the associations, you can do
column.each do |key, value|
Column.new(
column_title: key
column_data_type: value
end
end
(and then save block)
(Both assuming that your fields are named column_title and not just title.)

Creating object in database without showing view to user

I have controller with action new, and I want it to create ActiveRecord::Base descendant object, and write it into database (without showing it to user).
def new
active_order = current_user.orders.find {|o| o.status > 0 }
active_order = Order.new if active_order.nil?
(...)
end
Order.new creates local object, but my question is -- how to make Rails to fill it with default values and write to database?
You can write an unsaved ActiveRecord object instance to the database with the save method:
active_order = Order.new if active_order.nil?
active_order.save
It won't fill in the columns automatically, however. If you want to populate it with default values you can do this before calling save:
active_order = Order.new if active_order.nil?
active_order.field_name = "foo"
active_order.save
Or you can pass it in when you call new:
active_order = Order.new(:field_name => "foo") if active_order.nil?
active_order.save
If you want to do something fancier, you can have Rails automatically populate fields when you save by adding something like this to the model:
before_validation_on_create :set_default_values
def set_default_values
field_name = "foo" if field_name.blank?
end
you can use Order.Create(...) which will create a new object and persist it in the database (assuming Order is a child of ActiveBase of course)

Resources