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] ?
Related
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
I need to modify the issue's start_date and due_date some how,
But I haven't used Rails before, so I don't know how the it run in server,
And I need to implement it in a short time.
So I add these code in the controller,
def date
issue = Issue.find(params[:id])
issue.start_date = params[:start_date]
issue.due_date = params[:due_date]
ntc_str = "Fail to save!" + params[:id]
if issue.save
ntc_str = 'Issue saved!'
end
flash[:notice] = ntc_str;
redirect_to :controller => 'gantts', :action => 'show', :project_id => params[:p_id]
end
It runs when I access it by myself
It always failed and the "ntc_str" always is "Fail to save!" if I use javascript to access it.
For example:
It runs when I input the url "http://xxx.xxx.xxx.xxx/date?id=6&project_id=test&start_date=2016-06-08&due_date=2016-06-30" by hands,
But it failed when I use javascript "window.location.href='/date?id=6&project_id=test&start_date=2016-06-08&due_date=2016-06-30'"
It runs when I input the params in the form I create and click to submit it,
But it failed when I use javascript "document.getElementById('start_date').value = '2016-06-30'; /..../ $('#test-form').submit()"
Could you tell me why it always fails and how can I use the issue model? I have be crazy now.
It would be useful, if you provide some logs with each cases you try.
Also, you can see what goes wrong with issue, when you try to save it, with:
if issue.save
ntc_str = 'Issue saved!'
else
Rails.logger.error(issue.errors.full_messages)
end
I am using Rails 4 i am trying to set an api and i have a services controller where i def some methods like this:
def articles_stores
#article = Store.find(params[:id])
if #article.nil?
render :json => {:error_msg => "Record not found",:error_code => 404,:success => false}
else
render json: {article: #article.as_json({except: [:updated_at,:created_at]}),success: true}
end
end
But for some reason it is not rendering the error the else part works fine y also have all the necessary routes
Any help will be appreciated
#article.nil? will never be true if the article does not exist: Store.find(params[:id]) will already raise an exception if the record does not exist, and this than gets handled by rails automatically as a 404. If you want to return nil, use something like this:
Store.where(id: 10).first
# old, deprecated way:
Store.find_by_id(10)
Also see here.
In our Rails 3.1.0 app, we need to modify params passed to rfq controller in create and update. For example, we want to record the current user id under input_by_id. What we did was:
#rfq.input_by_id = session[:user_id]
It worked as expected. Also when need_report field is false, then report_language field should be nil. We decide to add the following line in rfq controller to make sure the nil is passed to report_language when need_report is false:
#rfq.report_language = nil unless params[:need_report]
However this addition causes the rspec case failure (in create/update of the controller) because of the data validation failure. However when we fire up the app, it behaves fine without saving the report_language when need_report is false. I am wondering if the line above is not the right way to use params[:need_report] for #rfq updating.
Thanks so much.
UPDATE:
Controller code:
def create
if has_create_right?
#rfq = Rfq.new(params[:rfq], :as => :roles_new )
#rfq.input_by_id = session[:user_id]
#save sales_id selected
if sales? && member? && !team_lead?
#rfq.sales_id = session[:user_id]
end
#view page may carry the hidden report language even if need_report == false
#rfq.report_language = nil unless params[:need_report]
#save into join table rfqs_standards
params[:rfq][:standard_ids].each do |sid|
#rfq.standards << Standard.find(sid.to_i) if !sid.nil? && sid.to_i > 0
end unless params[:rfq][:standard_ids].nil?
#save into join table rfqs_test_items
params[:rfq][:test_item_ids].each do |tid|
#rfq.test_items << TestItem.find(tid.to_i) if !tid.nil? && tid.to_i > 0
end unless params[:rfq][:test_item_ids].nil?
if #rfq.save!
redirect_to URI.escape("/view_handler?index=0&msg=RFQ saved!")
else
flash.now[:error] = "RFQ not saved!"
render 'new'
end
else
redirect_to URI.escape("/view_handler?index=0&msg=No rights!")
end
end
Test case failed after addition of #rfq.report_language = nil unless params[:need_report]
it "should be successful for corp head" do
session[:corp_head] = true
session[:user_id] = 1
s = Factory(:standard)
rfq = Factory.attributes_for(:rfq, :need_report => true, :report_language => 'EN')
rfq[:standard_ids] = [s.id] # attach standard_id's to mimic the POST'ed form data
get 'create', :rfq => rfq
#response.should redirect_to URI.escape("/view_handler?index=0&msg=RFQ saved!")
response.should render_template('new')
end
the problem ist that you are simply not looking at the right value.
get 'create', :rfq => rfq will result in a params-hash like {:rfq => {...}}
so you need to #rfq.report_language = nil unless params[:rfq][:need_report] == 'true'
I am trying to test my update action in Rails with this:
context "on PUT to :update" do
setup do
#countdown = Factory(:countdown)
#new_countdown = Factory.stub(:countdown)
put :update, :id => #countdown.id, :name => #new_countdown.name, :end => #new_countdown.end
end
should_respond_with :redirect
should_redirect_to("the countdowns view") { countdown_url(assigns(:countdown)) }
should_assign_to :countdown
should_set_the_flash_to /updated/i
should "save :countdown with new attributes" do
#countdown = Countdown.find(#countdown.id)
assert_equal #new_countdown.name, #countdown.name
assert_equal 0, (#new_countdown.end - #countdown.end).to_i
end
end
When I actually go through the updating process using the scaffold that was built it updates the record fine, but the tests give me this error:
1) Failure:
test: on PUT to :update should save :countdown with new attributes. (CountdownsControllerTest)
[/test/functional/countdowns_controller_test.rb:86:in `__bind_1276353837_121269'
/Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `call'
/Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `test: on PUT to :update should save :countdown with new attributes. ']:
<"Countdown 8"> expected but was
<"Countdown 7">.
I'd have thought those end columns would screw up ruby, but looks like it doesn't maybe...?
Anyways, So I assume that /test/functional/countdowns_controller_test.rb:86 is this assertion: assert_equal #new_countdown.name, #countdown.name.
So your assertion is asking that #new_countdown.name and #countdown.name are the same? Open the factory and see, i guess is the easy answer. not sure what you are trying to test here.
also, why is #countdown = Countdown.find(#countdown.id) using the same instance variable name? Isn't #countdown in setup the same as #countdown in your find line in the test?