How to exclude an exception from rails

New to rails and error handling issues.

I have the following code in a helper in the / lib folder that is used to apply a coupon to a certain price.

As you can see from the code below, I am trying several different ways to return any errors. In particular, model.errors.add and flash [: error].

The code below should refund the new price after applying a valid coupon. What am I not sure about what to return in case of an error? I cannot return false as I am below because this method should indeed return a numeric value, not a boolean. What is the best way to output an error to the view from lib / helper and stop execution?

def calc_coupon transaction_price, discount_code, current_tenant
   coupon = Coupon.where(code: discount_code, tenant_id: current_tenant.id).first

   if coupon.nil?
     current_tenant.errors.add :base, "No such coupon exists"
     return false
   else
     if coupon.maxed? || coupon.expired?
       flash[:error] = "This coupon is no longer valid."
       return false
     else
      transaction_price = coupon.calculate(transaction_price)
     end
   end
   return transaction_price
end

      

+3


source to share


1 answer


I think you are on the right track, but you are struggling because you are mixing view logic with business logic. Forget the helper class and add functionality to your model.

By adding validations to the model, not the helper class, and adding errors to base your controller action, it can handle errors when it tries to save / create a record in the normal way, thus "triggering errors"

eg.

    class MyModel < ActiveRecord ...
      validate :validate_coupon
      protected
        def validate_coupon
#          add your validations here - call your boolean check which adds errors to the model
#          if validations are ok then calculate your transaction price here
        end
    end

      

If you have your checks as public methods on your model, then they can be called from anywhere, that is, from the view, so the public method transaction_price on your model, which first checks for errors and sets the price, can be used like this

<%= @some_object.transaction_price %>

      

As I said earlier, since validations are added to the model, then your controller action just needs to check the call value to save and flash errors as usual if it returns false, so there is no need to do anything in the action controller at all. Just the default behavior.

There are a mountain of verification options available to you. Take a look at http://guides.rubyonrails.org/active_record_validations_callbacks.html

Hope this makes sense.

UPDATE IN ANSWER ON COMMENT

1) You have it quite a lot. This is just an assumption that you can calculate the transaction price as part of the verification. If it doesn't meet your requirement, then that's fine. I was just trying to give you the best fit for your question. The point is that you must have two separate instance methods, then you can call them anywhere, which makes it much easier to experiment and move things around. Go with your suggested solution. Good:)

2) Take a close look at the forest controller and views. You will see how the fail / updates fail (they automatically fail if you add errors as part of the validation process) using an if statement and you will see how the partial _form information displays the errors

This is an example of a form that deals with engines in an application I'm working on.



<%= form_for(@engine) do |f| %>
  <% if @engine.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@engine.errors.count, "error") %> prohibited this engine from being saved:</h2>

      <ul>
      <% @engine.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

      

Basically, it checks for errors and loops through all of them, if any, in order to display them.

To handle flash messages you have to use the main app layout The following code displays flash messages inside a div called "info" which you can style but want

  <% unless flash.empty? %>
    <div class="info">
    <%- flash.each do |name, msg| -%>
      <%= content_tag :div, msg, :id => "flash_#{name}" %>
    <%- end -%>
    </div>
  <% end %>

      

By placing this before the main crop in your application.html.erb in the views / layouts folder you can be sure every page will render, flash messages will always be shown to the user

Of course, if you are displaying flash messages, you may not need to display errors. Sometimes both are right, and sometimes just one or the other is perfect. This is for you to decide on a case-by-case basis.

The main point is that the receiving action for HTTP POST or HTTP PUT (create or update action) handles the inability to save with or without flash as you see fit e.g. Create Action

  def create
    @engine = Engine.new(params[:engine])

    respond_to do |format|
      if @engine.save
        format.html { redirect_to @engine, notice: 'Engine was successfully created.' }
        format.json { render json: @engine, status: :created, location: @engine }
      else
        flash.now[:notice] = "Failed to save!" # flash.now because we are rendering. flash[:notice] if using a redirect
        format.html { render action: "new" }
        format.json { render json: @engine.errors, status: :unprocessable_entity }
      end
    end
  end

      

Hope that clears things up.

UPDATE 2 Forgot to mention that flash is just a hash and uses a session cookie, so you can set any key you like for example.

flash.now[:fred]

      

This will allow you to style and handle a specific fred notification in a specific view if you want a different style to be done with your app's layout by adding code to handle the key: fred (or name of your choice) to a specific view

+3


source







All Articles