Dealing with array while using to_json - ruby-on-rails

I have just started learning ruby on rails and I was using this code to get the json representation of the question model
post "api/quizzes/addquestion/#{#quiz.id}",
{ question:
{ text: "Question Text Example", mark: 2, choices:["a","b","c","d"], right_answer: "a" }
}.to_json
I got :
choices: [] , empty array
I don't know the reason for this as all other attributes are sent succesfully. What is the solution for such problem
I am using this while testing the following method:
def add_question
question = Question.new(question_params)
quiz = Quiz.find(params[:quiz_id])
if question.save
quiz.questions << question
render json: { success: true, data:{:question => question}, info:{} }, status: 201
else
render json: { success: false, data:{}, :info => question.errors }, status: 422
end
end
def question_params
params.require(:question).permit(:text, :mark, :choice, :right_answer)
end
the test :
class AddingQuestionsTest < ActionDispatch::IntegrationTest
setup {#quiz = Quiz.create(name: 'Quiz1', subject: 'physics', duration: 10, no_of_MCQ: 5, no_of_rearrangeQ: 5)}
setup {#question = Question.create(text: "Question Text Example", mark: 2, choices:["a","b","c","d"], right_answer: "a")}
test 'successes to add question to a quiz' do
post "api/quizzes/addquestion/#{#quiz.id}",
{ question:
{ text: "Question Text Example", mark: 2, choices:["a","b","c","d"], right_answer: "a" }
}.to_json,
{ 'Accept' => Mime::JSON, 'Content-Type' => Mime::JSON.to_s }
question_response = json(response.body)
assert_equal 201, response.status
assert_equal #quiz.questions.first, #question
assert_equal publish_response[:success], true
end
end
and this is the question model:
class Question < ActiveRecord::Base
serialize :choices,Array
belongs_to :quiz
end
When I run the test I get failure in:assert_equal #quiz.questions.first, #question
choices: []
choices: ["a", "b", "c", "d"]

Just we need to change
def question_params
params.require(:question).permit(:text, :mark, :choice, :right_answer)
end
to
def question_params
params.require(:question).permit(:text, :mark, :right_answer, :choice => [])
end

Related

Rails 5.1.: strong params for JSON POST request

I have JS where data are posted with Ajax and in terminal my params look like this:
Started POST "/strongbolt/user_groups" for 10.0.2.2 at 2017-06-27 16:27:23 +0000
Processing by Strongbolt::UserGroupsController#create as JSON
Parameters: {"strongbolt_user_group"=>{"name"=>"Some test group",
"description"=>"Some test description", "user_ids"=>{"0"=>{"id"=>"3"},
"1"=>{"id"=>"2"}, "2"=>{"id"=>"5"}}, "role_ids"=>{"0"=>{"id"=>"1"},
"1"=>{"id"=>"2"}}}}
My Create action looks like this:
def create
user_roles #Helper method
#user_group = Strongbolt::UserGroup.create!(user_group_params)
respond_to do |format|
format.js { flash.now[:notice] = "User group #{#user_group.name} created!" }
format.json { render json: {
data: #user_group.as_json(only: [:id, :name, :description], include: {
users: { only: [:id, :name] }, roles: {only: [:id, :name] }}),
}
}
end
end
private
def user_group_params
params.require(:strongbolt_user_group)
.permit(:name, :description, {user_ids: []}, {role_ids: []})
end
In my terminal I can see it create name and description, but does not insert user_ids and role_ids. So far I've been trying differently with params, but no luck - can't make them to be saved.
I get this error: Unpermitted parameters: :user_ids, :role_ids
How do I make all params to be saved, please? Thank you!
instead of
{
"strongbolt_user_group"=>{
"name"=>"Some test group",
"description"=>"Some test description",
"user_ids"=>{"0"=>{"id"=>"3"}, "1"=>{"id"=>"2"}, "2"=>{"id"=>"5"}},
"role_ids"=>{"0"=>{"id"=>"1"}, "1"=>{"id"=>"2"}}
}
}
you should send
{
"strongbolt_user_group"=>{
"name"=>"Some test group",
"description"=>"Some test description",
"user_ids"=>["3", "2", "5"],
"role_ids"=>["1", "2"]
}
}
EDIT
If you can't change the format of received params, you could do something like the following:
private
def user_group_params
modified_params.require(:strongbolt_user_group)
.permit(:name, :description, {user_ids: []}, {role_ids: []})
end
def modified_params
user_ids = params[:strongbolt_user_group][:user_ids].values.map(&:values).flatten
role_ids = params[:strongbolt_user_group][:role_ids].values.map(&:values).flatten
ActionController::Parameters.new({
strongbolt_user_group: params[:strongbolt_user_group].except(:user_ids, role_ids).merge(user_ids: user_ids, role_ids: role_ids)
})
end

Params for json array in Rails

I'm new to rails,
Please check my code and tell me whats wrong with my use params, because this is how it made sense to me.
Controller:
def create
user = User.find(user_params)
order = user.purchases.new
render json: order.errors if !order.save
basket = params.require(:basket)
basket.each do |b|
i = Item.find(b[:item_id])
render json: i.errors, status: 422 if !i
order.purchases_items.create(item_id: i, quantity: b[:quantity])
end
render nothing: true, status: 201 # location: show action
end
and my test file is sending
test "making order" do
post "/api/users/#{#tuser.id}/orders",
{ basket: [ { item_id: '2', quantity: '5' },
{ item_id: '1', quantity: '4'} ] }.to_json,
{ 'Accept' => Mime::JSON, 'Content-Type' => Mime::JSON.to_s }
assert_response 201
assert_equal Mime::JSON, response.content_type
end
Thanks,
What I basically want to do is store each array element in the array basket from params[:basket], and iterate over it.
Sometime params keys are not get converted into symbols automatically. Can u try passing string "basket" instead of symbol :basket?

How to use nested_attributes when processing JSON?

I'm trying to write an update method that processes JSON. The JSON looks like this:
{
"organization": {
"id": 1,
"nodes": [
{
"id": 1,
"title": "Hello",
"description": "My description."
},
{
"id": 101,
"title": "fdhgh",
"description": "My description."
}
]
}
}
Organization model:
has_many :nodes
accepts_nested_attributes_for :nodes, reject_if: :new_record?
Organization serializer:
attributes :id
has_many :nodes
Node serializer:
attributes :id, :title, :description
Update method in the organizations controller:
def update
organization = Organization.find(params[:id])
if organization.update_attributes(nodes_attributes: node_params.except(:id))
render json: organization, status: :ok
else
render json: organization, status: :failed
end
end
private
def node_params
params.require(:organization).permit(nodes: [:id, :title, :description])
end
I also tried adding accepts_nested_attributes_for to the organization serializer, but that does not seem to be correct as it generated an error (undefined method 'accepts_nested_attributes_for'), so I've only added accepts_nested_attributes_for to the model and not to the serializer.
The code above generates the error below, referring to the update_attributes line in the update method. What am I doing wrong?
no implicit conversion of String into Integer
In debugger node_params returns:
Unpermitted parameters: id
{"nodes"=>[{"id"=>101, "title"=>"gsdgdsfgsdg.", "description"=>"dgdsfgd."}, {"id"=>1, "title"=>"ertret.", "description"=>"etewtete."}]}
Update: Got it to work using the following:
def update
organization = Organization.find(params[:id])
if organization.update_attributes(nodes_params)
render json: organization, status: :ok
else
render json: organization, status: :failed
end
end
private
def node_params
params.require(:organization).permit(:id, nodes_attributes: [:id, :title, :description])
end
To the serializer I added root: :nodes_attributes.
It now all works, but I'm concerned about including the id in node_params. Is that safe? Wouldn't it now be possible to edit the id of the organization and node (which shouldn't be allowed)? Would the following be a proper solution to not allowing it to update the id's:
if organization.update_attributes(nodes_params.except(:id, nodes_attributes: [:id]))
looks super close.
Your json child object 'nodes' need to be 'nodes_attributes'.
{
"organization": {
"id": 1,
"nodes_attributes": [
{
"id": 1,
"title": "Hello",
"description": "My description."
},
{
"id": 101,
"title": "fdhgh",
"description": "My description."
}
]
}
}
You can do this sort of thing. Put this in your controller.
before_action do
if params[:organization]
params[:organization][:nodes_attributes] ||= params[:organization].delete :nodes
end
end
It will set the correct attribute in params and still use all the accepts_nested_attributes features.

Issue with apipie gem and rspec in rails 4

i'm writing the code to get my Rspec tests to pass on my api. I'm using the apipie gem to generate documentation and it seems that my tests are failing because thy are expecting a number and it's funny because this is exactly what I want to test.
The page fails when the :bpm parameter is not a number. is there any way of going around this ?
context "when is not created" do
before(:each) do
user = FactoryGirl.create :user
#invalid_lesson_attributes = { title: "California Dreamin",
bpm: "Hello"
}
request.headers['Authorization'] = user.auth_token
post :create, { user_id: user.id, lesson: #invalid_lesson_attributes }
end
it "renders an errors json" do
lesson_response = json_response
expect(lesson_response).to have_key(:errors)
end
it "renders the json errors on why the user could not be created" do
lesson_response = json_response
expect(lesson_response[:errors][:bpm]).to include "is not a number"
end
it { should respond_with 422 }
end
end
Update spec:
context "when is not updated" do
before(:each) do
patch :update, { user_id: #user.id, id: #lesson.id,
lesson: { bpm: "ten" }, format: :json }
end
it "renders an errors json" do
lesson_response = json_response
expect(lesson_response).to have_key(:errors)
end
it "renders the json errors on why the user could not be updated" do
lesson_response = json_response
expect(lesson_response[:errors][:bpm]).to include "is not a number"
end
it { should respond_with 422 }
end
in my users_controller:
api :POST, '/teachers/:user_id/lessons/', "Create lesson"
param :lesson, Hash, desc: 'Lesson information', :required => true do
param :title, String, desc: 'Title of the lesson', :required => true
param :bpm, :number, desc: 'tempo of the lesson (beats per second)', :required => true
end
error :code => 422, :desc => "Unprocessable Entity"
my error when I run my rspec tests :
Apipie::ParamInvalid: Invalid parameter 'bpm' value "Hello": Must be a number.
Adds format json to post request
post :create, { user_id: user.id, lesson: #invalid_lesson_attributes, format: :json }
That worked for me.

Create Nested Object with Rails 4 (JSON call)

I am refactoring a project, and I remembered that I had some troubles in realizing how to put a nested object, but I found this question useful.
So, as I understand it, you needed to pass as a parameter your associated model name in plural and add a '_attributes' to it. It worked great in Rails 3.2.13.
Now, here is what I have in Rails 4:
class TripsController < Api::V1::ApiController
def create
begin
#user = User.find(params[:user_id])
begin
#campaign = #user.campaigns.find(params[:campaign_id])
if #trip = #campaign.trips.create(trip_params)
render json: #trip, :include => :events, :status => :ok
else
render json: { :errors => #trip.errors }, :status => :unprocessable_entity
end
rescue ActiveRecord::RecordNotFound
render json: '', :status => :not_found
end
rescue ActiveRecord::RecordNotFound
render json: '', :status => :not_found
end
end
private
def trip_params
params.require(:trip).permit(:evnt_acc_red, :distance, events_attributes: [:event_type_id, :event_level_id, :start_at, :distance])
end
end
And the Trip model looks like this:
class Trip < ActiveRecord::Base
has_many :events
belongs_to :campaign
accepts_nested_attributes_for :events
end
So, I am doing a POST call with the following JSON:
{"trip":{"evnt_acc_red":3, "distance":400}, "events_attributes":[{"distance":300}, {"distance":400}]}
And, even though I don't get any kind of error, no event is being created. The trip is being created correctly, but not the nested object.
Any thoughts on what should I do to make this work on Rails 4?
Alright, so... I was sending the JSON wrongly:
Instead of:
{
"trip": {
"evnt_acc_red": 3,
"distance": 400
},
"events_attributes": [
{
"distance": 300
},
{
"distance": 400
}
]
}
I should have been sending:
{
"trip": {
"evnt_acc_red": 3,
"distance": 400,
"events_attributes": [
{
"distance": 300
},
{
"distance": 400
}
]
}
}

Resources