I have been using attachment_fu on a project for a long time and all was fine but now as I am trying to bring the project up to rails 2.3.3 I am running into a strange bug that is driving me nuts. The Attachment, a logo in this case, validates correctly on create but does not fail validation on update. I have debugged it and it fails the intial validate but does not seem to throw an exception or at least not one that is caught by my rescue in the controller. Seems like I have tried everything but can't figure this one out.
Controller:
# POST /tournaments
# POST /tournaments.xml
def create
# Build tournament
#tournament = Tournament.new(params[:tournament].merge(:user_id => current_user.id) )
# Save the uploaded attachments
params[:uploads].each do |upload|
#tournament.documents << Document.new({:uploaded_data => upload[:document]}.merge(:description => upload[:description]))
end unless params[:uploads].nil?
# if supplied save an event logo
#logo = Logo.new({:uploaded_data => params[:logo][:upload_data]}) unless params[:logo].nil? or params[:logo][:upload_data].blank?
#tournament.logo = #logo unless #logo.nil?
respond_to do |format|
begin
Tournament.transaction do
#tournament.logo.save! unless #tournament.logo.nil?
#tournament.save!
end
flash[:notice] = 'Tournament was successfully created.'
format.html { redirect_to tournament_url(#tournament) }
format.xml { head :created, :location => tournament_url(#tournament) }
rescue
flash[:notice] = 'Errors prevented your Tournament from being saved'
format.html { render :action => "new" }
format.xml { render :xml => #tournament.errors, :status => :unprocessable_entity }
end
end
end
# PUT /tournaments/1
# PUT /tournaments/1.xml
def update
#tournament = Tournament.find(params[:id])
#tournament.user_id = session[:orig_user_id]
respond_to do |format|
begin
Tournament.transaction do
# Update Logo if necessary
unless params[:logo][:upload_data].blank?
#tournament.logo.destroy unless #tournament.logo.nil?
#tournament.logo = Logo.new({:uploaded_data => params[:logo][:upload_data]}.merge(:user_id => current_user.id))
end
# Save any uploaded documents
params[:uploads].each do |upload|
#tournament.documents << Document.new({:uploaded_data => upload[:document]}.merge(:description => upload[:description]))
end unless params[:uploads].nil?
# Update Tournamnet Attributes
#tournament.attributes = params[:tournament]
# Save the Tournament
#tournament.save!
end
flash[:notice] = 'Tournament was successfully updated.'
format.html { redirect_to tournament_url(#tournament) }
format.xml { head :ok, :location => tournament_url(#tournament) }
rescue
flash[:notice] = 'Errors prevented your Tournament from being updated'
format.html { render :action => "edit" }
format.xml { render :xml => #tournament.errors, :status => :unprocessable_entity }
end
end
end
Logo Model:
class Logo < Asset
validate_on_create :attachment_valid?
has_attachment :content_type => :image,
:storage => :file_system,
:max_size => 4.megabytes,
:resize_to => '810x150>',
:processor => :ImageScience,
:thumbnails => { :thumb => '270x50>' }
def attachment_valid?
content_type = attachment_options[:content_type]
unless content_type.nil? || content_type.include?(self.content_type)
errors.add(:upload_data, " * must be an image file (jpg, gif, or png)")
end
size = attachment_options[:size]
unless size.nil? || size.include?(self.size)
errors.add(:upload_data, "* image must be 4MB or less")
end
end
before_thumbnail_saved do |thumbnail|
record = thumbnail.parent
thumbnail.user_id = record.user_id
thumbnail.listing_id = record.listing_id
end
end
I am running the following
Rails 2.3.3
image_science 1.2.0
Thanks
--Tim
You could also use a :before_save callback to test the object. If it is invalid, raise an exception.
try adding:
validate_on_update :attachment_valid?
Related
Question is I have two models the first is called project and the second is status. Project accepts nested attr for my status model. That being said when I do an if statement in my project create action it seems like it isn't working. I dont get any errors and it doesnt send out my action mailer method. Any help would be greatly appreciated.
Here is my project model
class Project < ActiveRecord::Base
has_many :status
accepts_nested_attributes_for :status, :allow_destroy => true
end
Here is my status model
class Status < ActiveRecord::Base
belongs_to :project
end
My status model actually has a column called status which is what I am trying to check on the create action in my project controller bellow.
Here is my project controller
class ProjectController < ApplicationController
def create
#project = Project.new(p_params)
respond_to do |format|
if #project.save
#these if statements are the ones that aren't working.
if #project.status == 'Production'
ProjectMailer.alert_production(#project).deliver
elsif #project.status == 'Design For Manufacturing'
ProjectMailer.alert_dfm(#project).deliver
elsif #project.status == 'Prototype'
ProjectMailer.alert_prototype(#project).deliver
elsif #project.status == 'Tooling'
ProjectMailer.alert_tooling(#project).deliver
elsif #project.status == 'Quality'
ProjectMailer.alert_quality(#project).deliver
end
format.html { redirect_to project_index_path, :notice => 'Project successfully created.' }
format.xml { render :xml => #project, :status => :created, :location => #project }
end
end
end
private
def p_params
params.require(:project).permit(:project_id, :in_visual, :planner, :part_num, :status, :customer, status_attributes: [:id, :a_d, :status, :note,
:process,
:sim_date,
:sim_reqs, :sim_def_quote, :tool_date, :tool_mat, :tool_o_source, :tool_insp, :tool_cust_paper, :tool_pro_ready, :pro_process, :pro_date])
end
end
If your project has many Status object attached to it, then you need to decide which one is the one you care about. Is it the first one, is it the oldest one, is it the newest one, etc. The way that your code is written, you are comparing a Status object to a string. Try something like this:
if #project.save
# which status do you care about?
status_that_matters = #project.statuses.first.status
if status_that_matters == 'Production'
ProjectMailer.alert_production(#project).deliver
elsif status_that_matters == 'Design For Manufacturing'
ProjectMailer.alert_dfm(#project).deliver
elsif status_that_matters == 'Prototype'
ProjectMailer.alert_prototype(#project).deliver
elsif status_that_matters == 'Tooling'
ProjectMailer.alert_tooling(#project).deliver
elsif status_that_matters == 'Quality'
ProjectMailer.alert_quality(#project).deliver
end
format.html { redirect_to project_index_path, :notice => 'Project successfully created.' }
format.xml { render :xml => #project, :status => :created, :location => #project }
end
Two other ways to do it:
if #project.save
# which status do you care about?
status_that_matters = #project.statuses.first.status
status_actions = {
'Production' => :alert_production,
'Design For Manufacturing' => :alert_dfm,
'Prototype' => :alert_prototype,
'Tooling' => :alert_tooling,
'Quality' => :alert_quality
}
action_method = status_actions[status_that_matters]
ProjectMailer.send action_method, #project
format.html { redirect_to project_index_path, :notice => 'Project successfully created.' }
format.xml { render :xml => #project, :status => :created, :location => #project }
end
Makes it a bit cleaner in my opinion but adds the obscurity of a dynamic method call. It would also be easy to put the status_actions Hash in a configuration file.
Another fun option nowadays in Ruby is to switch on the String:
if #project.save
# which status do you care about?
status_that_matters = #project.statuses.first.status
case status_that_matters
when 'Production'
ProjectMailer.alert_production(#project).deliver
when 'Design For Manufacturing'
ProjectMailer.alert_dfm(#project).deliver
when 'Prototype'
ProjectMailer.alert_prototype(#project).deliver
when 'Tooling'
ProjectMailer.alert_tooling(#project).deliver
when 'Quality'
ProjectMailer.alert_quality(#project).deliver
end
format.html { redirect_to project_index_path, :notice => 'Project successfully created.' }
format.xml { render :xml => #project, :status => :created, :location => #project }
end
I think I would prefer the last one!
I have the following code segment
def range
respond_to do |format|
if params[:start] && params[:end]
begin
dstart = Time.parse(params[:start])
dend = Time.parse(params[:end])
rescue => e
format.json { render :json => { :status => :unprocessable_entity, :error => e.message }} and return
end
...
And it works totally fine and makes it to the stuff at the bottom...
...
format.json { render :json => { :status => :ok, :posts => #res.to_json(:only => [:date, :content, :user_id]) } }
else
format.json { render :json => { :status => :unprocessable_entity, :error => "must have a _start_ and _end_ date" } }
...
The problem is when an exception occurs and the rescue section is called, Rails does not respond with json but instead tells me "Template Missing." Is something wrong with my syntax?
D'oh, turns out I don't need the format.json bit. Why, exactly, though?
Consider this example regarding show action to understand the error
class ModelsController
.
.
def show
#model = Model.find(params[:id])
respond_to do |format|
format.html
format.js
end
end
end
In this case, if the request is of type html then rails by convention searcher for a file
app/views/models/show.html.erb.
But if the request is of type js then rails will search for app/views/models/show.js.erb.
If these files does not exist then rails will throw template missing error
so if you are only responding to json then you can do
respond_to do |format|
format.json do
begin
..
rescue
render :json => { }
end
end
I'm using ActiveMerchant with Authorize.net and already setup 2 models Order and Transaction. (http://railscasts.com/episodes/145-integrating-active-merchant)
I would like to get error message from the transaction if error occurred then output in flash message. Here is my code:
#order_controller.rb
def create
#order = Order.new(params[:order])
respond_to do |format|
if #order.save
if #order.purchase
format.html { redirect_to(#order, :notice => 'Order was successfully created.') }
format.xml { render :xml => #order, :status => :created, :location => #order }
else
flash[:error] = #order.transactions.response
format.html { render :action => "new" }
end
else
format.html { render :action => "new" }
format.xml { render :xml => #order.errors, :status => :unprocessable_entity }
end
end
end
And here is my transaction model:
#models/order_transaction.rb
class OrderTransaction < ActiveRecord::Base
belongs_to :order
serialize :params
def response=(response)
self.success = response.success?
self.authorization = response.authorization
self.message = response.message
self.params = response.params
rescue ActiveMerchant::ActiveMerchantError => e
self.success = false
self.authorization = nil
self.message = e.message
self.params = {}
end
end
The transaction data has been saved to the database as below:
#models/order.rb
class Order < ActiveRecord::Base
has_many :transactions, :class_name => "OrderTransaction"
attr_accessor :card_type, :card_number, :card_month, :card_year, :card_verification
def purchase
response = GATEWAY.purchase(charge_amount, credit_card, purchase_options)
transactions.create!(:action => "purchase", :amount => charge_amount, :response => response)
response.success?
end
...
end
I want to flash up this transaction error message when transaction failed. And what would I should do to get these errors if I have 2 transactions for one order.?
Any help would be much appreciated.
Thanks!
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
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.