Rails 3: Custom Model Notices - ruby-on-rails

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

Related

Rails - How to accept an array of JSON objects

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

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

The action could not be found for controller

Having gone through my code I have a separate problem from my original question and rather than writing a new question. I will leave the old part at the bottom of this and post the new problem here. I do this because they are closely related.
New:
Im getting an error message saying
Unknown action
The action 'response' could not be found for CrawlerController
I'll keep it simple but the code for model, controller and routes are below in the previous question.
A basic run down is response is a def within CrawlerController as is add_Request.
The routes are matched as such:
match "/requests/new" => "crawler#add_Request"
match 'requests/:id' => 'crawler#response'
Here is controller code as per user request:
class CrawlerController < ApplicationController
def add_Request
#request = Request.new(params[:request])
respond_to do |format|
if #request.save
format.html { redirect_to(#request, :notice => 'Request was successfully created.') }
format.xml { render :xml => #request, :status => :created, :location => #request }
else
format.html { render :action => "new" }
format.xml { render :xml => #request.errors, :status => :unprocessable_entity }
end
end
end
def response
#request = Request.find(params[:id])
respond_to do |format|
format.html
format.js { render :json => #request }
end
end
def show
#request = Request.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #request }
format.json{
render :json => #request.to_json
}
end
end
end
please recheck code of controller as I can see it
class CrawlerController < ApplicationController
def add_Request
#request = Request.new(params[:request])
respond_to do |format|
if #request.save
format.html { redirect_to(#request, :notice => 'Request was successfully created.') }
format.xml { render :xml => #request, :status => :created, :location => #request }
else
format.html { render :action => "new" }
format.xml { render :xml => #request.errors, :status => :unprocessable_entity }
end
end
def response
#request = Request.find(params[:id])
respond_to do |format|
format.json {render :#request.to_json}
end
end
so one end is missing an your response action is defined inside add_Request

How to create a new object in Rails with a predefined property

I have a Rails app that has a bunch of pages, each page has many convos. On each page there's a link to create a new convo on that page. This is the code for that link:
<%= link_to 'New Convo', new_convo_path(:page=>#page) %>
However, on the next page, "convo/new" the page property is empty. What am I missing?
EDIT here are my new and create functions for convos
def new
#convo = Convo.new(params[:page])
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #convo }
end
end
# POST /convos
# POST /convos.xml
def create
#convo = Convo.new(params[:convo])
respond_to do |format|
if #convo.save
format.html { redirect_to(#convo, :notice => 'Convo was successfully created.') }
format.xml { render :xml => #convo, :status => :created, :location => #convo }
else
format.html { render :action => "new" }
format.xml { render :xml => #convo.errors, :status => :unprocessable_entity }
end
end
end
You need to load the page ... try to set a before filter ...
before_filter :find_page
private
def find_page
#page = Page.find(params[:page_id])
end
Then you build using nested resources
def new
#convo = #page.convos.build
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #convo }
end
end
def create
#convo = #page.convos.build(params[:convo])
.....
end
My guess is that you are missing a ":page=>"
def new
#convo = Convo.new(:page=>params[:page])
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #convo }
end
end

Sqlite on rails problem with relations

I have a problem with relations while using sqlite3 on rails.
First i build my scaffolds,
add the references to migration files,
add belongs_to has_many to models
than get my database up and runinig with basic rake db:migrate command.
And then it doesn't work,
I guess there is a missing step which i cannot figure out :S
By the way i am tryng to implement the example talewiki on Building Dynamic Web 2.0
Websites with Ruby on Rails, i am at page 75.
The example is on mysql.
class GenresController < ApplicationController
# GET /genres
# GET /genres.xml
def index
#genres = Genre.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #genres }
end
end
# GET /genres/1
# GET /genres/1.xml
def show
#genre = Genre.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #genre }
end
end
# GET /genres/new
# GET /genres/new.xml
def new
#genre = Genre.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #genre }
end
end
# GET /genres/1/edit
def edit
#genre = Genre.find(params[:id])
end
# POST /genres
# POST /genres.xml
def create
#genre = Genre.new(params[:genre])
respond_to do |format|
if #genre.save
format.html { redirect_to(#genre, :notice => 'Genre was successfully created.') }
format.xml { render :xml => #genre, :status => :created, :location => #genre }
else
format.html { render :action => "new" }
format.xml { render :xml => #genre.errors, :status => :unprocessable_entity }
end
end
end
# PUT /genres/1
# PUT /genres/1.xml
def update
#genre = Genre.find(params[:id])
respond_to do |format|
if #genre.update_attributes(params[:genre])
format.html { redirect_to(#genre, :notice => 'Genre was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #genre.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /genres/1
# DELETE /genres/1.xml
def destroy
#genre = Genre.find(params[:id])
#genre.destroy
respond_to do |format|
format.html { redirect_to(genres_url) }
format.xml { head :ok }
end
end
end
The error is occurring on this line:
#genre = Genre.find(params[:id])
giving
ActiveRecord::RecordNotFound in
GenresController#show Couldn't find
Genre with ID=tales
That means that params[:id] has the value "tales" which is wrong. I'm guessing here, but I bet that somewhere in the form or elsewhere there is an attempt to do a GET on /genre/tales instead of /tales/genre/:id (where :id should be an integer). I'm also guessing you have a mapping in routes.rb like:
map.resources :tales, :has_many => :genres
I don't have a copy of the book you're following.

Resources