Goal: To check if a record has already been updated and either allow or not allow the record to be updated if it already has been.
This is in case a buyer is on a page that doesn't have updated information and attempts to cancel an order once it's already been completed.
I have the following code, which works but also doesn't work correctly:
private
def prevent_order_update
#order = Order.find(params[:id])
if ( #order.order_status[2] || #order.order_status[3] )
redirect_to #order, notice: "Your request status for Order:#{#order.id} has already been updated."
end
end
with:
before_action :prevent_order_update, :only => [:update]
This works, but also "works" if the :order_status is 1, which is shouldn't.
I only want a block in the update IF the order status is anything but 1.
The order status is from a model enum of 1,2,3.
I have also tried using:
if ( #order.order_status[2] || #order.order_status[3] ) && #order.order_status_previously_changed?
which completely blocks the prevent_order_update from working all together.
And:
( #order.order_status[2] || #order.order_status[3] ) != #order.order_status[1]
Which then blocks my update method all together and still gives me the prevent_order_update method notice when the order status is 1
#order.order_status is corresponding to string when it comes to rails enums.
In your case, say #order.order_status is charged. When you execute #order.order_status[2] it actually produces a which is the third item of charged string. In this case the comparison always returns true.
So try the following code:
def prevent_order_update
#order = Order.find(params[:id])
if ( #order.charged? || #order.canceled? )
redirect_to #order, notice: "Your request status for Order:#{#order.id} has already been updated."
end
end
You can use aasm gem, so that you no need any before_action. You can solve it in model level using aasm transitions
https://github.com/aasm/aasm
Related
Goal: dynamically update another Model's properties (Tracker) from Controller (cards_controller.rb), when cards_controller is running the def update action.
Error I receive : NameError in CardsController#update, and it calls out the 2nd last line in the
def update_tracker(card_attribute) :
updated_array = #tracker.instance_variable_get("#{string_tracker_column}")[Time.zone.now, #card.(eval(card_attribute.to_s))]
Perceived problem: I have everything working except that I don't know the appropriate way to 'call' the attribute of Tracker correctly, when using dynamic attributes.
The attribute of the Tracker is an array (using PG as db works fine), I want to
figure out what property has been changed (works)
read the corresponding property array from Tracker's model, and make a local var from it. (works I think, )
push() a new array to the local var. This new array contains the datetime (of now) and, a string (with the value of the updated string of the Card) (works)
updated the Tracker with the correct attribute.
With the following code from the cards_controller.rb
it's the if #card.deck.tracked in the update method that makes the process start
cards_controller.rb
...
def update
#card = Card.find(params[:id])
if #card.deck.tracked
detect_changes
end
if #card.update_attributes(card_params)
if #card.deck.tracked
prop_changed?
end
flash[:success] = "Card info updated."
respond_to do |format|
format.html { render 'show' }
end
else
render 'edit'
end
end
...
private
def detect_changes
#changed = []
#changed << :front if #card.front != params[:card][:front]
#changed << :hint if #card.hint != params[:card][:hint]
#changed << :back if #card.back != params[:card][:back]
end
def prop_changed?
#changed.each do |check|
#changed.include? check
puts "Following property has been changed : #{check}"
update_tracker(check)
end
end
def update_tracker(card_attribute)
tracker_attribute = case card_attribute
when :front; :front_changed
when :back; :back_changed
when :hint; :hint_changed
end
string_tracker_column = tracker_attribute.to_s
#tracker ||= Tracker.find_by(card_id: #card.id)
updated_array = #tracker.instance_variable_get("#{string_tracker_column}")[Time.zone.now, #card.(eval(card_attribute.to_s))]
#tracker.update_attribute(tracker_attribute, updated_array)
end
Edit: For clarity here's the app/models/tracker.rb:
class Tracker < ActiveRecord::Base
belongs_to :card
end
Your use of instance_variable_get has been corrected, however this approach is destined to fail because ActiveRecord column values aren't stored as individual instance variables.
You can use
#tracker[string_column_changed]
#card[card_attribute]
To retrieve attribute values by name. If you want to get an association, use public_send. The latter is also useful if there is some accessor wrapping the column value (eg carrierwave)
From your error it seem your issue is this:
#tracker.instance_variable_get("#{string_tracker_column}")
evaluates to this after string interpolation:
#tracker.instance_variable_get("front_changed")
which is incorrect use of instance_variable_get. It needs an # prepended:
#tracker.instance_variable_get("#front_changed")
Seems like using instance_variable_get is unnecessary, though, if you set attr_reader :front_changed on the Tracker model.
I'm creating an app that lets users purchase items from an online store. I followed the RailsCasts episodes, and built my OrdersController like so.
def create
#order = current_cart.build_order(order_params)
#order.ip_address = request.remote_ip
if #order.save
if #order.purchase
Item.where(email: Order.last.email).last.purchased == true
PurchaseMailer.confirmation_email(Item.last.email).deliver
flash[:notice] = "Thanks for your purchase"
redirect_to root_path
else
flash[:danger] = "Something was wrong"
redirect_to :back
end
else
render :action => 'new'
end
end
I recently decided to add an attribute to my items, which says whether or not they've been purchased or not. Items in the cart have not yet been purchased. I created a migration, giving all items a purchased attribute, that is a boolean.
By default, items are not purchased, so the default value is false.
class AddPurchasedToItem < ActiveRecord::Migration
def change
add_column :items, :purchased, :boolean, :default => false
end
end
That's why I added this line of code to my Orders#Create action.
Item.where(email: Order.last.email).last.purchased == true
Here I was setting the value of purchased from false to true. However, when I load up rails console
Item.last.purchased
=> false
It looks like the value still isn't being stored
As another response points out, you're using the == to assign a value, which isn't right. You need = instead.
And you have to save an item after you assign a value to it.
An example:
conditions = {email: Order.last.email} # using your conditions
item = Item.find_by(conditions)
item.purchased = true
item.save # this is what you're missing
Item.find(item.id).purchased # will be true
Another way to update is the following:
item.update_attribute(:purchased, true)
Yet another way is to call update_all on the ActiveRecord::Relation object like so:
# update all items that match conditions:
Item.where(conditions).update_all(purchased: true)
Which method you choose may depend on the scenario as update_all doesn't run the callbacks you specify in the model.
In your case however, all you're missing is the item.save line.
Item.where(email: Order.last.email).last.purchased == true
You're using a == operator to try to assign a value. Try using = instead.
I have a Rails 4.0.2 controller action which is called by a method submission. Inside this method I check for a particular field to match a criteria and in case it is not matched, I want to present an error to the user and call render new again, without going any further within the method. So I wrote:
discount_coupon = DiscountCoupon.where(code: discount_code).first
if discount_coupon.nil? || (discount_coupon.present? && !discount_coupon.currently_active?)
flash[:alert] = t("flash.entries.create.invalid_coupon_code")
render :new
return
end
#entry.save
So, in my app when the discount_coupon variable is wrong and the program enters the conditional, it should halt its execution on return but somehow it does not and the #entry.save method is called and screws up the whole thing.
What am I doing wrong? I appreciate any help!
why don't you just enclose #entry.save with an else condition and remove return? so that if the discount coupon condition fails it renders the action :new else it just saves the entry.
As it's been suggested, try if/else conditional:
discount_coupon = DiscountCoupon.where(code: discount_code).first
if discount_coupon.nil? || (discount_coupon.present? && !discount_coupon.currently_active?)
flash[:alert] = t("flash.entries.create.invalid_coupon_code")
render :new
else
#entry.save
end
you can refactor this code once you are sure it is working.
I am writing a little plugin for my company redmine to assign unique documents [progressive] codes
the code I wrote so far works, but I don't think it is multi threads proof as there is a chance two users get the same document code.
I would like to find a way to lock the table while getting the last number and creating the new record with the incremented document number (D9999)
This is the active record:
class Documenti < ActiveRecord::Base
unloadable
def self.nextest
record=self.last
if (record.nil?) then ultimo=sprintf("D0000")
elsif (record.codice.nil?) then ultimo=sprintf("D0000")
else ultimo=record.codice
end
if (/^D[0-9]{4}/ =~ ultimo) == 0 then
c=ultimo.split("D")
p=c[1].to_i + 1
t=sprintf("D%04i",p)
end
return t
end
end
Controller then is like this:
def new
#documenti = Documenti.new
#documenti.codice=Documenti.nextest
respond_to do |format|
format.html # new.html.erb
end
end
so far the nextest value is not saved and another user can take the same value.
I'm trying to figure out an inconsistency between what's happening in a functional test and what is happening in my development environment. I have a custom validation method unique_entry that is essentially a specialized version of validates_uniqueness_of. It looks like this:
def unique_entry
matched_entry = Entry.first(:conditions => ['LOWER(field_one) = LOWER(?) AND LOWER(field_two) = LOWER(?)', self.field_one, self.field_two])
errors.add_to_base('Duplicate detected') if matched_entry && (matched_entry.id != self.id)
end
The update action in the controller is very basic:
def update
if #entry.update_attributes(params[:entry])
flash.now[:success] = 'Success'
render :action => 'show'
else
flash.now[:error] = 'Error'
render :action => 'edit'
end
end
This works just fine when I'm creating a new record. When I update a record, however, I get inconsistent behavior. If I test it from a browser in my development environment, it correctly renders the edit action with an error message, but in my functional test, it accepts the update as successful. Here is the test:
test "should not update entry and should render edit view if invalid update" do
put :update, { :id => 1, :field_one => 'new_value', :field_two => 'new_value' } # 'new values' are the same as another existing record to trigger the duplication check
assert_template :edit
assert_not_nil flash[:error]
end
I looked at the test log and discovered that the values unique_entry is using are the record's original values instead of the values it should be attempting to update with. That is, the first line of unique_entry generates an SQL query like this:
SELECT * FROM "entries" WHERE (LOWER(field_one) = LOWER('original_value_of_field_one') AND LOWER(field_two) = LOWER('original_value_of_field_two')) LIMIT 1
What am I missing here? Why do my validations seem to be running against the original record instead of the new values only in the test environment?
In your test, shouldn't there be some reference to :entry, since that is what you are looking for in the controller params[:entry] ?