I meet a trouble about hash in ruby on rails - ruby-on-rails

i have a trouble about hash in rails
example:
grades = {"a" => 10, "b" => 6}
grades = {"c"=>15, "d"=>16}
respond_to do |format|
format.json { render json: grades }
end
but result is received : "c"=>15, d"=>16
why doesn't it return all : a,b,c,d.

You are overriding your variable. You need either do:
grades = { "a" => 10, "b" => 6 }
grades.merge!({"c"=>15, "d"=>16})
or
grades = { "a" => 10, "b" => 6 }
grades['c'] = 15
greade['d'] = 16
for your 2nd problem in the comment:
I guess you want something like this:
records['data'] = []
(0..2).each do |i|
records['data'] << [#items_record[i]['id'], #items_record[i]['id'], #items_record[i]['name_vi']]
end

Related

Passing hashes into create action in Ruby on Rails

I'm trying to create a workout routine that gets created when a workout gets created by passing this in via ajax:
Parameters: {"utf8"=>"✓", "workout"=>{"name"=>"cool workout", "desc"=>"super cool"}, "exerciseorder"=>["4", "2"], "repssets"=>{"0"=>["40", "4"], "1"=>["60", "6"]}}
Here is what my Create action looks like in my Workout Controller:
exercise_order = params[:exerciseorder]
repssets = params[:repssets]
#workout = Workout.new(workout_params)
if #workout.save
WorkoutRoutine.create(
[
exercise_order.each_with_index.map { |x,i|
{
:exercise_id => x,
:position => i,
:workout_id => #workout.id
}
},
repssets.map { |x| x.last }.each { |y|
{
:reps => y.first,
:sets => y.last
}
}
]
)
render :nothing => true
else
render json: #workout.errors.full_messages, status: :unprocessable_entity
end
If I use an opening and closing '[]' within the WorkoutRoutine.create, it tells me:
ArgumentError: When assigning attributes, you must pass a hash as an argument.
And when I change them to '{}' it tells me:
syntax error, unexpected ',', expecting =>
I've tried a myriad of different combinations and work-arounds but can't seem to figure out why it won't correctly parse the data and save it to the database, any help is very appreciated.
EDIT:
When I remove the initial {} and [] from the WorkoutRoutine.create:
WorkoutRoutine.create(
exercise_order.each_with_index.map { |x,i|
{
:exercise_id => x,
:position => i,
:workout_id => 20
}
},
repssets.map { |x| x.last }.each { |y|
{
:reps => y.first,
:sets => y.last
}
}
)
I get this error message:
ArgumentError: wrong number of arguments (2 for 0..1)
Edit2:
This is the jQuery code that sents to the data field via ajax:
var getId = $(".exercise-list").sortable('toArray');
ary = []
$(getId).each(function () {
id = $(this[0]).selector;
var reps = $("#reps" + id).val();
var sets = $("#sets" + id).val();
ary.push([reps, sets])
});
var orderRepsSets = { exerciseorder: getId, repssets: ary }
var formData = $('#new_workout').serialize();
var data = formData + '&' + $.param(orderRepsSets);
$.ajax({
url: $("#new_workout").attr('action'),
method: 'POST',
data: data,
success: (function() {
....
});
Did I get it correctly that you want to create multiple WorkloadRoutine objects, one for each exercise with the corresponding repetitions, the position, etc. If yes, then in order to do that you will have to pass an array of hashes (one hash for each object) to the WorkoutRoutine.create() function. As engineersmnky correctly stated in his comment, the data structure you are currently passing is more like [[{...},{...},{...}],[{...},{...},{...}]], but instead it should be just [{...},{...},...]. Do achieve that, something like this should do the trick:
WorkoutRoutine.create(
exercise_order.map.with_index { |x,i|
{
:exercise_id => x,
:position => i,
:workout_id => #workout.id,
:reps => repssets[i.to_s].first,
:sets => repssets[i.to_s].last
}
}
)
If you could change repssets to an array like exercise_order you could even remove the string cast for getting the reps and sets, which would simplify the whole think even more.
If it comes for errors they are quite self explanatory. But let's start from beginning..
I assume that WorkoutRoutine is an ActiveRecord::Base model. The WorkoutRoutine.create method gets 0 or 1 argument which should be a Hash or a block.
In the first iteration you were passing an Array instead of Hash, so it looked like:
WorkoutRoutine.create([a, b]) # => ArgumentError: When assigning attributes, you must pass a hash as an argument.
On the second iteration you stripped away the square brackets, what gave you 2 arguments instead of one Hash:
WorkoutRoutine.create(a, b) # => ArgumentError: wrong number of arguments (2 for 0..1)
If you read errors carefully you will start getting the idea what's happening.
About the workout routine itself
From what you specified I would assume that you want something like:
Workout has many Routines
Routine belongs to Workout and Exercise
Routine is composed of fields like
position/order,
number of repetitions,
number of sets
If my assumption is correct, then you want to use nested_attributes and then have parameters and controller like
# given parameters as workout_params
{
workout: {
name: "workout name",
desc: "workout description",
routines_attributes: [
{ position: 1, exercise_id: 4, repetitions_number: 40, sets_number: 4 },
{ position: 2, exercise_id: 2, repetitions_number: 60, sets_number: 6 }
]
}
}
# Controller
def create
#workout = Workout.new workout_params
if #workout.save
redirect_to # ...
else
render :new
end
end
private
def workout_params
params.require(:workout).permit(:name, :desc, routines_attributes: [:position, :exercise_id, :repetitions_number, :sets_number])
end
It should be strait forward how to then create a view with fields_for and update models with proper associations

Convert active record result into Json object

I have below output from active record query
[{"image_id"=>1, "image_name"=> "image1", action_type"=>"Call", "count"=>2},
`{"image_id"=>1, "image_name"=> "image1","action_type"=>"sms", "count"=>1},
{"image_id"=>2, "image_name"=> "image2","action_type"=>"sms", "count"=>1} ]`
Now I want this to be converted into Json object like below
{ "1": { "counts": { "call": 2, "sms": 1 } , "title":'image1' },
"2": { "counts": {"sms": 1} , 'title':'image2'}}
Please check this code.
#xx = [{"image_id"=>1, "image_name"=>"image1", "action_type"=>"Call", "count"=>2}, {"image_id"=>1, "image_name"=>"image1", "action_type"=>"sms", "count"=>1}, {"image_id"=>1, "image_name"=>"image1", "action_type"=>"sms", "count"=>1}]
#arr = []
#xx.each_with_index do |x, i|
#arr << {(i+1).to_s.to_sym => {"counts" => {x["action_type"].to_sym => x["count"]}}}
end
respond_to do |f|
f.json {render :json => #arr}
end
There are two popular libraries that are both very helpful:
active_model_serializers
jbuilder
I prefer active_model_serializers, personally. Many disagree.

Rails 3 : Generate view using rabl

In my Rails Application i have Two instance variables #departments and #register
#departments =
{
"users": [
{
"departmentid": "DP11"
},
{
"departmentid": "DP12"
},
{
"departmentid": "DP13"
},
{
"departmentid": "DP10"
}
]
}
#register =
{
"attendance": [
0,
0,
2,
1
]
}
#register contains an array .
Is it possible to show like below format using rabl (attendancebydepartment.json.rabl) view
{
"users": [
{
"departmentid": "DP11",
"attendance"=0
},
{
"departmentid": "DP12",
"attendance"=0
},
{
"departmentid": "DP13",
"attendance"=2
},
{
"departmentid": "DP10",
"attendance"=1
}
]
}
My controller looks like
def attendancebydepartment
#register = Array.new
#departments = User.select('departmentid').uniq
startdate = params[:startdate]
enddate = params[:enddate]
#count = #departments.count
#departments.each do |d|
#register << (Register.where(:date => startdate..enddate , :departmentid => d.departmentid).sum(:one))+(Register.where(:date => startdate..enddate , :departmentid => d.departmentid).sum(:two))
end
end
Is it possible to add each department and its corresponding attendance to array,so that i can display like expected format.
Perhaps use the zip method to create a new variable and then present that.
irb(main):001:0> departments = {'users' => [{'id' => 1}, {'id' => 2}]}
=> {"users"=>[{"id"=>1}, {"id"=>2}]}
irb(main):002:0> register = {'attendance' => [0,1]}
=> {"attendance"=>[0, 1]}
irb(main):004:0> departments['users'].zip(register['attendance'])
=> [[{"id"=>1}, 0], [{"id"=>2}, 1]]
On the other hand, it looks like a simpler design would be to have a Department model that has a has_many association with Users. Then you could refer to the count of users directly from an instance of Department.
It may be easiest to create objects in your controller using OpenStruct, something like this, but I would recommend re-writting attendancebydepartment to not loop twice.
#users = []
#departments.each_with_index do |dep, index|
user = OpenStruct.new
user.departmentid = dep.departmentid
user.attendence = #register[index].attendence
#users << user
end
And in the view:
collection #users => :users
attribute :departmentid, :attendence

JSON Rendering: How to include the opening bracket description?

I have some Ruby code that is putting out JSON like this:
def nearby
##bathrooms = Bathroom.geo_scope(:origin =>[params[:lat],[params[:lon]]], :within => 5)
lat = params[:lat]
lon = params[:lon]
##bathrooms = Bathroom.geo_scope(:within => 5, :origin => [45.580639,-122.677682], :order=>'distance')
#bathrooms = Bathroom.geo_scope(:within => 3, :origin => [lat,lon], :order=>'distance')
respond_to do |format|
format.json { render :json => #bathrooms }
format.js { render :nothing => true }
end
end
The result is something like this:
[{"ID":129,"access":"1","avail":"0","bathroomtype":"0","city":"PORTLAND","comment":"","country":"US","created":"0000-00-00 00:00:00","directions":"The two bathrooms are in the long hallway at the back of the restaurant, past the bar.","distance":"2.94986114636676","lat":"45.539217","lon":"-122.661795","modifed":"0000-00-00","name":"Echo","postal":"97212-3727","slug":"echo-portland170","source":"","state":"OR","street":"2225 NE Martin Luther King Jr. Blvd"},
What I don't know how to do is to name the set? For example, after the first enclosing bracket I would like to name the set bathrooms. How can I do this is Rails?
Try:
render json: { bathrooms: #bathrooms }
It looks like #bathrooms is a list, so you can't assign keys to elements in lists. You'll need to make a map first. For example, if you only want the first element:
format.json { render :json => {:bathrooms => #bathrooms[0]} }
To set all list elements:
format.json { render :json => {:bathrooms => #bathrooms} }

Remove blank element from array

When I'm saving multiple select from a ruby on rails form it appears to be adding a blank element at the front. How do I remove it? The field is selected_player.
{"utf8"=>"✓",
"authenticity_token"=>"H8W7qPBezubyeU0adnTGZ4oJqYErin1QNz5oK0QV6WY=",
"schedule"=>{"event"=>"1",
"result_id"=>"",
"time"=>"26/10/2012",
"duration"=>"15",
"arrival_time"=>"14",
"location_id"=>"25",
"selected_players"=>["", "38", "41"],
"team_id"=>"1",
"opponent_id"=>"7",
"home_or_away"=>"Home"},
"commit"=>"Save Event"}
controller
def update
#schedule = Schedule.find(params[:id])
#user = User.find(current_user)
#players = User.where(:team_id => current_user[:team_id]).all
respond_to do |format|
if #schedule.update_attributes(params[:schedule])
Notifier.event_added(#user,#schedule).deliver
format.html { redirect_to(#schedule,
:notice => "#{event_display_c(#schedule.event)} vs #{#schedule.opponent.name} was successfully updated.") }
format.json { head :no_content }
else
format.html { render :action => "edit" }
format.json { render :json => #schedule.errors, :status => :unprocessable_entity }
end
end
end
This works for empty strings:
array.delete_if(&:empty?)
To filter out empty strings and nil values use:
array.delete_if(&:blank?)
Example:
>> a = ["A", "B", "", nil]
=> ["A", "B", "", nil]
>> a.delete_if(&:blank?)
=> ["A", "B"]
Ref reject! of Array class
params["schedule"]["selected_players"] = ["", "38", "41"]
params["schedule"]["selected_players"].reject!{|a| a==""} #gives params["selected_players"] = ["38", "41"]
This should work as well.
params["schedule"]["selected_players"].reject!(&:blank?)
Something like:
params["selected_players"].select!{|val| !val.empty?}
should work
What is "selected_players"? Is it something like "collection_singular_ids" of the collection associations? If so, you can leave it as it is, because ActiveRecord will remove the blank elements from the array with following code:
ids = Array.wrap(ids).reject { |id| id.blank? }
If you want to handle this in the model rather than the controller you can add a setter method like this
def selected_players=(param_array)
write_attribute(:selected_players, param_array.reject(&:blank?))
end
I think params["selected_players"].compact is the most succinct.
Docs are here: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-compact

Resources