Rails - How to accept an array of JSON objects - ruby-on-rails

How do I accept an array of JSON objects on my rails site? I post something like
{'team':{'name':'Titans'}}
However, if I try to post a JSON with an array of objects. It only saves the 1st object.
{'team':[{'name':'Titans'},{'name':'Dragons'},{'name':'Falcons'}]}
My goal is to send multiple 'teams' in 1 JSON file. What do I have to write on the Rails side?
On the rails side, I have something like
def create
#team = Team.new(params[:team])
#team.user_id = current_user.id
respond_to do |format|
if #team.save
format.html { redirect_to(#team, :notice => 'Team was successfully created.') }
format.json { render :json => #team, :status => :created, :location => #team }
else
format.html { render :action => "new" }
format.json { render :json => #team.errors, :status => :unprocessable_entity }
end
end
end
Do I take the params: and for each element, create a new team or something? I'm new to ruby so any help would be appreciated.

Let me assume you post
{'team':[{'name':'Titans'},{'name':'Dragons'},{'name':'Falcons'}]}
Then your params will be
"team" => {"0"=>{"chapter_name"=>"Titans"}, "1"=>{"chapter_name"=>"Dragons"}, "2"=>{"chapter_name"=>"Falcons"}}
My idea is
def create
#insert user id in all team
params[:team].each_value { |team_attributes| team_attributes.store("user_id",current_user.id) }
#create instance for all team
teams = params[:team].collect {|key,team_attributes| Team.new(team_attributes) }
all_team_valid = true
teams.each_with_index do |team,index|
unless team.valid?
all_team_valid = false
invalid_team = teams[index]
end
end
if all_team_valid
#teams = []
teams.each do |team|
team.save
#teams << team
end
format.html { redirect_to(#teams, :notice => 'Teams was successfully created.') }
format.json { render :json => #teams, :status => :created, :location => #teams }
else
format.html { render :action => "new" }
format.json { render :json => invalid_team.errors, :status => :unprocessable_entity }
end
end

Related

Add default table field when creating new obect Ruby on Rails 3

My models are: Projects has_many Feeds. I just added a column to my Feeds table called feed_error. I currently have a form on the app that creates a new Feed when entered. I want to be able to set feed_error to false by default. In my feeds_controller, I have my create method:
def create
#feed = Project.find(params[:project_id]).feeds.build(params[:feed])
respond_to do |format|
if #feed.save
format.html { redirect_to( :back, :notice => 'Feed was successfully created.') }
format.xml { render :xml => #feed, :status => :created, :location => [#feed.project, #feed] }
else
format.html { render :action => "new" }
format.xml { render :xml => #feed.errors, :status => :unprocessable_entity }
end
end
end
I was thinking I could try adding :feed_error => 'false' to the params, but that doesn't seem to work. How do I set this field by default?
You have a couple options. In your controller you can do:
def create
#feed = Project.find(params[:project_id]).feeds.build(params[:feed])
#feed.feed_error = false
respond_to do |format|
if #feed.save
format.html { redirect_to( :back, :notice => 'Feed was successfully created.') }
format.xml { render :xml => #feed, :status => :created, :location => [#feed.project, #feed] }
else
format.html { render :action => "new" }
format.xml { render :xml => #feed.errors, :status => :unprocessable_entity }
end
end
end
You could also set this up in your database migration. For example, if you don't need a null value and instead want the default to be false you can add:
t.boolean "feed_error", :null => false
to your migration.

Rails3 Conditional Statements in Controller Actions

I'm trying to write a conditional statement in my tasks controllers for a simple rails3 application.
Users have many tasks and tasks have one user.
When creating a task, we can chose who owns it:
<%= collection_select(:task, :user_id, User.all, :id, :name, {:prompt => true}) %>
I want the system to send an email to the owner of the task, only when it's created for someone else. I.e. I do not need to receive an email when I create a task for myself.
My mailer's working fine and in my tasks controller, I've tried this:
def create
#task = Task.new(params[:task])
respond_to do |format|
if #task.save and #task.user_id = current_user.id
format.html { redirect_to(tasks_path, :notice => 'Task was successfully created.') }
format.xml { render :xml => #task, :status => :created, :location => #task }
elsif #task.save
format.html { redirect_to(tasks_path, :notice => 'Task was successfully created.') }
format.xml { render :xml => #task, :status => :created, :location => #task }
TaskMailer.new_task(#task).deliver
else
format.html { render :action => "new" }
format.xml { render :xml => #task.errors, :status => :unprocessable_entity }
end
end
end
But it's not really working... Any chance of some assistance.
Replace #task.user_id = current_user.id with #task.user_id == current_user.id.
This is not the cause of your error, but you're saving your task two times if #task.user_id != current_user.id. You could do something like this instead:
def create
#task = Task.new(params[:task])
respond_to do |format|
if #task.save
format.html { redirect_to(tasks_path, :notice => 'Task was successfully created.') }
format.xml { render :xml => #task, :status => :created, :location => #task }
TaskMailer.new_task(#task).deliver if #task.user_id != current_user.id
else
format.html { render :action => "new" }
format.xml { render :xml => #task.errors, :status => :unprocessable_entity }
end
end
end
end
Are you not storing the id of the creator? If you do, all the data you need is in the model. Thus just implement an private instance method in Task model. Something like the following
# Task model
private
def notify_assignee
if new_record? || user_id_changed? && creator_id != user_id
TaskMailer.new_task(#task).deliver
end
end
Call the above method after_save
# Task model
after_save :notify_assignee
In case you are not storing the creator_id in the database, create an attribute accesor called :creator_id.
# Task model
attr_accessor :creator_id
In the controller, before saving, do
# Tasks controller
#task.creator_id = current_user.id
and the above method would still work.
You controller re directions would automatically be simplified to
if #task.save
format.html { redirect_to(tasks_path, :notice => 'Task was successfully created.') }
format.xml { render :xml => #task, :status => :created, :location => #task }
else
format.html { render :action => "new" }
format.xml { render :xml => #task.errors, :status => :unprocessable_entity }
end
And also this would be the right way to go as the "business logic" (which in your case is send assignee an email notifying that someone else has assigned a task to them) would reside in the model.
It's because of this line, you're using an assignment = instead of the comparision ==
Also the current_user should be #current_user instead because you didn't specify it in the method. (Or you have a method current_user() that you did not post. Then it's fine)
if #task.save and #task.user_id = current_user.id
It should be
if #task.save and #task.user_id == #current_user.id
also imho you should move the mailing-stuff to the Task-model and use an after_save-callback.
Maybe you need change this line: #task.user_id = current_user.id to #task.user_id == current_user.id

Rails 3: Custom Model Notices

Currently I verify that there are no duplicated Members when trying to create a new Member and add it to a Team.
members_controller.rb
def create
#team = current_team
player = Player.find(params[:player_id])
#member = #team.add_player(player.id)
respond_to do |format|
if #member.save
format.html { redirect_to(#team, :notice => 'Member was successfully added.') }
format.js { #current_member = #member }
format.xml { render :xml => #member,
:status => :created, :location => #member }
else
format.html { redirect_to(#team, :notice => 'Member already exists.') }
format.xml { render :xml => #member.errors,
:status => :unprocessable_entity }
end
end
end
team.rb
def add_player(player_id)
current_member = members.build(:player_id => player_id)
current_member
end
I want to add some logic to my add_player method in team.rb that checks various properties of the player that is being added. This action will require multiple failure messages, other than 'Member already exists.' How do I do this in the Model layer?
You can create custom errors on ActiveRecord models. These custom errors can have their own messages, which you can query in your controller if the save is not successful:
# app/models/team.rb
def add_player(player_id)
current_member = members.build(:player_id => player_id)
errors.add(:player_id, 'Custom error message here') if condition
errors.add(:base, 'Custom error message here') if condition
current_member
end
# app/controllers/members_controller.rb
def create
#team = current_team
player = Player.find(params[:player_id])
#member = #team.add_player(player.id)
respond_to do |format|
if #member.save
format.html { redirect_to(#team, :notice => 'Member was successfully added.') }
format.js { #current_member = #member }
format.xml { render :xml => #member,
:status => :created, :location => #member }
else
format.html { redirect_to(#team, :notice => #member.errors.full_messages) }
format.xml { render :xml => #member.errors,
:status => :unprocessable_entity }
end
end
end
More information on custom ActiveRecord validation errors here: http://api.rubyonrails.org/v2.3.8/classes/ActiveRecord/Errors.html#M001725
The controller logic to display all errors from base worked. However, I was not able to add errors from the add_player method as Ben suggested. I instead created separate custom validations as such:
Team.rb
validate validation_name
def validation_name
if condition
errors.add_to_base "Error Message"
end
end

Rails - Controller :notice - Add a variable?

I'm green behind the ears, but had a basic question about modifying the scaffolding's :notice to add a variable. For example, rails created the following create method for me:
def create
#order = Order.new(params[:order])
respond_to do |format|
if #order.save
format.html { redirect_to(#order, :notice => 'Order was successfully created.') }
format.xml { render :xml => #order, :status => :created, :location => #order }
else
format.html { render :action => "new" }
format.xml { render :xml => #order.errors, :status => :unprocessable_entity }
end
end
end
What I'm looking to do is add a variable to :notice so that it would print specifically what order was created (or edited with the update method). I tried some basic things using such as passing <%= order.id %>,though I felt this seemed unnatural within the controller?
Is adding a dynamic value possible within this format of this scaffolding? Or is it against the convention.
I appreciate the help, sorry if this is very newbish.
Beestings are the preferred way to insert dynamic values into strings in ruby. So if you wanted #order.id in your :notice, you could do this:
def create
#order = Order.new(params[:order])
respond_to do |format|
if #order.save
format.html { redirect_to(#order, :notice => "Order id # #{#order.id} was successfully created.") }
format.xml { render :xml => #order, :status => :created, :location => #order }
else
format.html { render :action => "new" }
format.xml { render :xml => #order.errors, :status => :unprocessable_entity }
end
end
end

swfupload 406 not acceptable error

def create
#asset = Asset.new(params[:asset])
respond_to do |format|
if params[:Filedata]
#asset = Asset.new :swf_uploaded_data => params[:Filedata]
#asset.user = current_user
#asset.save!
format.html { render :text => #asset.image.url(:thumb) }
format.xml { render :nothing => true }
else
if #asset.save
flash[:notice] = 'Created'
format.html { redirect_to(#asset) }
format.xml { render :xml => #asset, :status => :created, :location => #asset }
else
format.html { render :action => "new" }
format.xml { render :xml => #asset.errors, :status => :unprocessable_entity }
end
end
end
end
I am creating an upload status bar with swfupload. At the end of the upload I get a 406 error.
Not necessarily your answer but I had the same problem with Plupload and it happens that the request format is nil.
It might not be the best way but one could do the following:
request.format ||= :xml
in order to provide some default format in the method. Hope it can help others.

Resources