rails custom validation not failing when it should - ruby-on-rails
This might not be the cleanest code yet, still quite new to Ruby...
I have the following spam check method in my message.rb model:
validate :no_spam?, :if => "sender_user_id != nil"
private
def no_spam?
#first easy spam detection, if the (hidden by css) company field is filled, it is spam for sure
if !company.blank?
errors.add(:body, I18n.t(:No_spam_allowed))
return false
end
#Mollom advanced spam detection
m = Mollom.new(:private_key => 'xxx',
:public_key => 'xxx')
#check content in case it is a first submit of the form
if captcha_session_id.blank?
content = m.check_content(:post_body => body,
:author_name => sender_name,
:author_mail => sender_email,
:author_ip => sender_ip)
else
#check captcha if the form was resumbitted after an unsure result
logger.debug "DEBUG: GOING TO CHECK CAPTCHA"
result = m.valid_captcha?(:session_id => captcha_session_id,
:solution => captcha_solution.chomp)
if result
logger.debug "DEBUG: TRUE -> GOOD CAPTCHA"
return true
else
logger.debug "DEBUG: FALSE -> BAD CAPTCHA"
return false
end
end
#returning the right values and error messages for different content check outcomes
logger.debug "DEBUG: I'M CONTINUING THE METHOD EXECUTION"
if content.spam?
logger.debug "DEBUG: SPAM DETECTED"
errors.add(:body, I18n.t(:No_spam_allowed))
return false
elsif content.unsure?
logger.debug "DEBUG: MESSAGE UNSURE - FAIL FORM BUT SHOW CAPTCHA"
errors.add(:captcha_solution, I18n.t(:Type_the_characters_you_see_in_the_picture_below))
self.captcha_image_url = m.image_captcha(:session_id => content.session_id)["url"]
self.captcha_session_id = content.session_id
return false
else
logger.debug "DEBUG: MESSAGE OK!"
return true
end
end
development.log
Processing MessagesController#create (for 127.0.0.1 at 2011-08-12 12:01:24) [POST]
Parameters: {"commit"=>"Verzend", "action"=>"create", "authenticity_token"=>"xxxxxxxxxxx", "locale"=>"nl", "controller"=>"messages", "message"=>{"sender_email"=>"[FILTERED]", "company"=>"", "body"=>"unsure", "sender_phone"=>"xxxx", "sender_name"=>"Admin ImmoNatie"}}
Message Columns (6.0ms) SHOW FIELDS FROM `messages`
User Columns (10.0ms) SHOW FIELDS FROM `users`
User Load (7.0ms) SELECT * FROM `users` WHERE (`users`.`id` = '1') AND (users.deleted_at IS NULL ) LIMIT 1
SQL (0.0ms) BEGIN
User Update (0.0ms) UPDATE `users` SET `updated_at` = '2011-08-12 10:01:25', `perishable_token` = 'xxxxxxxxxxx', `last_request_at` = '2011-08-12 10:01:25' WHERE `id` = 1
SQL (3.0ms) COMMIT
SQL (0.0ms) BEGIN
DEBUG: I'M CONTINUING THE METHOD EXECUTION
DEBUG: MESSAGE UNSURE - FAIL FORM BUT SHOW CAPTCHA
SQL (0.0ms) ROLLBACK
Rendering template within layouts/application
Rendering messages/new
Rendered messages/_form (8.0ms)
Rendered layouts/_google_analytics (0.0ms)
Rendered layouts/_login (3.0ms)
Rendered layouts/_navigation (6.0ms)
Rendered layouts/_header (12.0ms)
Rendered about_us/_ten_reasons_9_body (0.0ms)
NewsletterEmail Columns (5.0ms) SHOW FIELDS FROM `newsletter_emails`
Rendered layouts/_footer (41.0ms)
Completed in 1907ms (View: 70, DB: 40) | 200 OK [http://infinitize.dynalias.com/contact]
tize.dynalias.com/contact]
SQL (0.0ms) SET SQL_AUTO_IS_NULL=0
Property Columns (10.0ms) SHOW FIELDS FROM `properties`
Processing MessagesController#create (for 127.0.0.1 at 2011-08-12 12:01:32) [POST]
Parameters: {"commit"=>"Verzend", "action"=>"create", "authenticity_token"=>"xxxxxxxxxxx", "locale"=>"nl", "controller"=>"messages", "message"=>{"sender_email"=>"[FILTERED]", "company"=>"", "body"=>"unsure", "captcha_solution"=>"", "sender_phone"=>"xx", "captcha_session_id"=>"xxxxxxxxxxx", "sender_name"=>"Admin ImmoNatie"}}
Message Columns (5.0ms) SHOW FIELDS FROM `messages`
User Columns (10.0ms) SHOW FIELDS FROM `users`
User Load (0.0ms) SELECT * FROM `users` WHERE (`users`.`id` = '1') AND (users.deleted_at IS NULL ) LIMIT 1
SQL (0.0ms) BEGIN
User Update (0.0ms) UPDATE `users` SET `updated_at` = '2011-08-12 10:01:32', `perishable_token` = 'xxxxxxxxxxx', `last_request_at` = '2011-08-12 10:01:32' WHERE `id` = 1
SQL (4.0ms) COMMIT
SQL (0.0ms) BEGIN
DEBUG: GOING TO CHECK CAPTCHA
DEBUG: FALSE -> BAD CAPTCHA
Message Create (0.0ms) INSERT INTO `messages` (`sender_email`, `receiver_user_id`, `receiver_email`, `created_at`, `body`, `opened_by_owner`, `updated_at`, `receiver_name`, `opened_by_sender`, `sender_ip`, `message_thread_id`, `sender_user_id`, `sender_name`) VALUES('admin#immonatie.be', 1, 'test#immonatie.be', '2011-08-12 10:01:33', 'unsure', NULL, '2011-08-12 10:01:33', 'ImmoNatie', NULL, '127.0.0.1', NULL, 1, 'Admin ImmoNatie')
MessageThread Columns (5.0ms) SHOW FIELDS FROM `message_threads`
MessageThread Create (1.0ms) INSERT INTO `message_threads` (`last_message_opened_by_sender_id`, `answered`, `initial_sender_user_id`, `created_at`, `initial_sender_email`, `starred`, `updated_at`, `last_message_opened_by_owner_id`, `type_id`, `owner_id`, `property_id`, `first_message_id`, `initial_sender_name`, `initial_sender_phone`, `last_message_id`, `last_message_added_at`) VALUES(213, NULL, 1, '2011-08-12 10:01:33', 'admin#immonatie.be', 0, '2011-08-12 10:01:33', 0, 174, 1, NULL, 213, 'Admin ImmoNatie', 'xx', 213, '2011-08-12 10:01:33')
Message Update (0.0ms) UPDATE `messages` SET `updated_at` = '2011-08-12 10:01:33', `message_thread_id` = 101, `sender_name` = 'Admin ImmoNatie', `created_at` = '2011-08-12 10:01:33', `sender_email` = 'admin#immonatie.be', `sender_ip` = '127.0.0.1', `sender_user_id` = 1, `receiver_name` = 'ImmoNatie', `receiver_email` = 'test#immonatie.be', `body` = 'unsure', `receiver_user_id` = 1 WHERE `id` = 213
Sent mail to test#immonatie.be
Date: Fri, 12 Aug 2011 12:01:33 +0200
From: Notifications <no-reply#immonatie.be>
To: test#immonatie.be
Subject: Nieuw contact bericht
Mime-Version: 1.0
Content-Type: text/html; charset=utf-8
Naam: xxxx<br />
E-mail: xxxx<br />
Telefoon: xxxx<br />
<br />
Bericht:<br />
unsure
SQL (3.0ms) COMMIT
In the development.log you see that I first submit the form (first create action) with 'unsure' in the body to invoke an unsure result from Mollom (this site is in development mode).
The validation fails for this first create action as expected.
But when I resubmit the form now with an empty captcha verification code, the validation method returns false, but the validation does not, since the save is continued and COMMITTED in the end.
Why does this validation not fail and break (rollback) the create action?
Thanks,
Michael
Found the problem. Returning false to the validation was not enough. I needed to add an error: errors.add(:body, "error message"). This error together with return false did the job.
Related
Rails - n+1 queries even though association is preloaded
I have a Person class that has many Book(s), and each Book has one Bookmark. In my code I'm doing something like this: items = author.books.includes(:bookmark) items.each { |item| generate_hash(item) } def generate_hash(item) { ... rest ..., finished: item.try(:finished?) } end class Book def finished? bookmark ? bookmark.page == self.pages : false end end Using item.try(:finished?) triggers a select query to get each individual bookmark even though I've preloaded them. However, if I change it to finished: item.is_a?(Book) ? item.finished? : nil it triggers only one query to get all the bookmarks just as I planned. Can anyone make sense of this? EDIT - adding the queries I'm getting in the log: with item.try(:finished?): Bookmark Load (2.7ms) SELECT `bookmarks`.* FROM `bookmarks` WHERE `bookmarks`.`deleted_at` IS NULL AND `bookmarks`.`book_id` = 41 LIMIT 1 Bookmark Load (2.3ms) SELECT `bookmarks`.* FROM `bookmarks` WHERE `bookmarks`.`deleted_at` IS NULL AND `bookmarks`.`book_id` = 42 LIMIT 1 Bookmark Load (3.9ms) SELECT `bookmarks`.* FROM `bookmarks` WHERE `bookmarks`.`deleted_at` IS NULL AND `bookmarks`.`book_id` = 43 LIMIT 1 Bookmark Load (3.3ms) SELECT `bookmarks`.* FROM `bookmarks` WHERE `bookmarks`.`deleted_at` IS NULL AND `bookmarks`.`book_id` = 44 LIMIT 1 # with item.is_a?(Book) ? item.finished? : nil Bookmark Load (2.6ms) SELECT `bookmarks`.* FROM `bookmarks` WHERE `bookmarks`.`deleted_at` IS NULL AND `bookmarks`.`book_id` IN (41, 42, 43, 44)
Rails PUT/POST request fails on first try, works on next
I have a VueJS Frontend application with a Ruby On Rails API backend. I need to change information about a user in the database. I have a PUT/POST request that removes the creditcard information. The basic rails code is below. The issue I am having is that I get the, very popular, error: NoMethodError (undefined method []' for nil:NilClass): But the catch is that it only happens on the first attempt. Subsequent attemps are successful. Can someone point me in the right direction here? Should you need additional informaiton, please don't hesitate to ask. class AccountsController < ApplicationController before_action :load_account, only: [:show, :update, :destroy, :formats, :charges, :destroy_card] def destroy_card #account.card_last_4 = nil #account.card_type = nil #account.card_exp_month = nil #account.card_exp_year = nil #account.plan_id = 1 #account.save! render json: { account: #account }, status: 200 end private def load_account if #current_user.admin? #account = Account.find(params[:id]) else #account = #current_user.accounts.find_by_id(params[:id]) unless #account.present? end end end Errors: Started PUT "//accounts/20/destroy_card" for ::1 at 2020-08-17 20:24:47 -0400 Processing by AccountsController#destroy_card as */* Parameters: {"id"=>20, "account_id"=>"20"} User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 10 LIMIT 1 ↳ app/controllers/application_controller.rb:18:in `load_user' AccountUser Load (0.2ms) SELECT `account_users`.* FROM `account_users` WHERE `account_users`.`user_id` = 10 AND `account_users`.`account_id` = 20 LIMIT 1 ↳ app/controllers/application_controller.rb:28:in `load_user' Account Load (0.1ms) SELECT `accounts`.* FROM `accounts` WHERE `accounts`.`id` = 20 LIMIT 1 ↳ app/controllers/application_controller.rb:30:in `load_user' CACHE Account Load (0.0ms) SELECT `accounts`.* FROM `accounts` WHERE `accounts`.`id` = 20 LIMIT 1 [["id", 20], ["LIMIT", 1]] ↳ app/controllers/accounts_controller.rb:97:in `load_account' (0.1ms) BEGIN ↳ app/models/account.rb:148:in `assign_plan' Plan Load (0.1ms) SELECT `plans`.* FROM `plans` WHERE `plans`.`id` = 1 LIMIT 1 ↳ app/models/account.rb:148:in `assign_plan' Account Update (0.2ms) UPDATE `accounts` SET `accounts`.`card_last_4` = NULL, `accounts`.`card_type` = NULL, `accounts`.`card_exp_month` = NULL, `accounts`.`card_exp_year` = NULL, `accounts`.`plan_id` = 1, `accounts`.`updated_at` = '2020-08-18 00:24:47' WHERE `accounts`.`id` = 20 ↳ app/controllers/accounts_controller.rb:82:in `destroy_card' Account Update (0.3ms) UPDATE `accounts` SET `accounts`.`plan_updated_at` = '2020-08-18 00:24:47' WHERE `accounts`.`id` = 20 ↳ app/models/account.rb:60:in `stripe_subscribe' (9.1ms) ROLLBACK ↳ app/controllers/accounts_controller.rb:82:in `destroy_card' Completed 500 Internal Server Error in 1150ms (ActiveRecord: 10.4ms | Allocations: 22601) NoMethodError (undefined method `[]' for nil:NilClass): app/models/account.rb:71:in `stripe_subscribe' app/controllers/accounts_controller.rb:82:in `destroy_card' Code from account.rb def stripe_subscribe s = stripe_customer.subscriptions.first self.plan_updated_at = Time.now save! if coupon.present? && plan.id != coupon.plan_id self.coupon = nil save! end if plan.price == 0 # Downgrading from a paid tier if s.present? meta = s.delete self.changed_plan = Plan.find(previous_changes[:plan_id][0]) self.changed_plan_date = Time.at(meta['current_period_end']).to_datetime save! end else s.delete({ prorate: true, invoice_now: true }) if s.present? params = { items: [{ plan: plan.short_name }] } if coupon.present? params[:coupon] = coupon.token end stripe_customer.subscriptions.create(params) if Rails.env == 'production' client = Slack::Web::Client.new client.chat_postMessage(channel: '#somechannel', text: "some random message", as_user: true) end end end
Spree currency convertor is not working for AED
I have included following gem in my gem file gem 'spree_multi_currency', github: 'spree/spree_multi_currency', branch: '2-3-stable' in my application following currencies are already present: SGD,USD,EUR,AUD,GBP,PHP,THB,MYR and these are converting price properly. But my requirement is to add AED currency to so I have added that also SGD,USD,EUR,AUD,GBP,PHP,THB,MYR,AED from backend Now I automatically got this option in my header now when i click on AED it gives me following error Started GET "/assets/world-globe.png" for 127.0.0.1 at 2016-01-26 10:26:08 +0100 Started POST "/currency/set" for 127.0.0.1 at 2016-01-26 10:26:11 +0100 Processing by Spree::CurrencyController#set as JSON Parameters: {"currency"=>"AED"} Spree::Country Load (1.1ms) SELECT "spree_countries".* FROM "spree_countries" WHERE "spree_countries"."name" = 'N/A' LIMIT 1 Spree::User Load (1.2ms) SELECT "spree_users".* FROM "spree_users" WHERE "spree_users"."id" = 1 ORDER BY "spree_users"."id" ASC LIMIT 1 Spree::Order Load (1.2ms) SELECT "spree_orders".* FROM "spree_orders" WHERE "spree_orders"."completed_at" IS NULL AND "spree_orders"."currency" = 'USD' AND "spree_orders"."guest_token" = 'ZtR5IUlQUC40ueZBlo21Pg' AND "spree_orders"."user_id" = 1 LIMIT 1 Spree::Adjustment Load (1.4ms) SELECT "spree_adjustments".* FROM "spree_adjustments" WHERE "spree_adjustments"."adjustable_type" = 'Spree::Order' AND "spree_adjustments"."adjustable_id" IN (6084) ORDER BY spree_adjustments.created_at ASC Spree::Order Load (0.9ms) SELECT "spree_orders".* FROM "spree_orders" WHERE "spree_orders"."user_id" = $1 AND "spree_orders"."completed_at" IS NULL AND (id != 6084) [["user_id", 1]] (0.9ms) BEGIN Spree::Order Exists (1.7ms) SELECT 1 AS one FROM "spree_orders" WHERE ("spree_orders"."number" = 'R501407003' AND "spree_orders"."id" != 6084) LIMIT 1 Spree::LineItem Load (1.3ms) SELECT "spree_line_items".* FROM "spree_line_items" WHERE "spree_line_items"."order_id" = $1 AND (currency != 'AED') ORDER BY created_at ASC [["order_id", 6084]] Spree::Variant Load (1.3ms) SELECT "spree_variants".* FROM "spree_variants" WHERE "spree_variants"."id" = $1 LIMIT 1 [["id", 2]] Spree::Price Load (1.3ms) SELECT "spree_prices".* FROM "spree_prices" WHERE "spree_prices"."deleted_at" IS NULL AND "spree_prices"."variant_id" = $1 AND "spree_prices"."currency" = 'AED' ORDER BY "spree_prices"."id" ASC LIMIT 1 [["variant_id", 2]] Spree::Product Load (1.3ms) SELECT "spree_products".* FROM "spree_products" WHERE "spree_products"."id" = $1 LIMIT 1 [["id", 2]] Spree::Product::Translation Load (1.3ms) SELECT "spree_product_translations".* FROM "spree_product_translations" WHERE "spree_product_translations"."spree_product_id" = $1 [["spree_product_id", 2]] (1.5ms) ROLLBACK Completed 500 Internal Server Error in 56ms RuntimeError - no AED price found for 28 Day Ultimate Teatox (28 Day): () Users/TopFormInvestment/.rvm/gems/ruby-2.1.4#skinnymint/bundler/gems/spree-cfe7e96539b6/core/app/models/spree/order/currency_updater.rb:34:in `update_line_item_price!' () Users/TopFormInvestment/.rvm/gems/ruby-2.1.4#skinnymint/bundler/gems/spree-cfe7e96539b6/core/app/models/spree/order/currency_updater.rb:18:in `block in update_line_item_currencies!' Please guide me how to solve this error. As I am new in spree
Go to the Products in admin panel. There you can see the price tab, click the tab and then you change the prices there itself.
You did not set an AED price for the variant 28 Day Ultimate Teatox. In update_line_items_price! it has a local variable price which returns the variants price in the newly set currency. If the price isn't present it will raise the RuntimeError you are getting. def update_line_item_currencies! line_items.where('currency != ?', currency).each do |line_item| update_line_item_price!(line_item) end end # Returns the price object from given item def price_from_line_item(line_item) line_item.variant.prices.where(currency: currency).first end # Updates price from given line item def update_line_item_price!(line_item) price = price_from_line_item(line_item) if price line_item.update_attributes!(currency: price.currency, price: price.amount) else raise RuntimeError, "no #{currency} price found for #{line_item.product.name} (#{line_item.variant.sku})" end end
Lock value of database column based on boolean value in Rails
I want to let a user edit the field report.plan only if report.published = false. If report.published = true and they try to save a change, I want to throw an error. I've written the following code to do this: class Report < ActiveRecord::Base validate :cannot_update_plan_after_published, on: :publish_plan! def publish_plan!(plan) self.plan = plan self.published = true self.save end private def cannot_update_plan_after_published if self.published? errors.add(:plan, "You cannot change the plan once it has been published.") end end end However, this is not working. When I call publish_plan! on an already published report, it makes the save. For example: > f = Report.last => #<Report id: 12, plan: "a", published: false> > f.publish_plan!("b") (0.1ms) begin transaction (0.4ms) UPDATE "reports" SET "plan" = 'b', "updated_at" = '2014-09-18 18:43:47.459983' WHERE "reports"."id" = 12 (9.2ms) commit transaction => true > f = Report.last Report Load (0.2ms) SELECT "reports".* FROM "reports" ORDER BY "reports"."id" DESC LIMIT 1 => #<Report id: 12, plan: "b", published: true> > f.publish_plan!("c") (0.1ms) begin transaction (0.4ms) UPDATE "reports" SET "plan" = 'c', "updated_at" = '2014-09-18 18:43:53.996191' WHERE "reports"."id" = 12 (8.7ms) commit transaction => true > Report.last Report Load (0.2ms) SELECT "reports".* FROM "reports" ORDER BY "reports"."id" DESC LIMIT 1 => #<Report id: 12, plan: "c", published: true> How do I get this field to become uneditable once report.published = true?
Try removing the on: :public_plan!. That way, the validation should be run every time the model is saved. validate :cannot_update_plan_after_published See here for more details: Adding a validation error with a before_save callback or custom validator? Also, for the validation method itself, change it to the following: def cannot_update_plan_after_published if self.published? && self.published_changed? == false errors.add(:plan, "You cannot change the plan once it has been published.") end end This allows you to set it the first time publishing the plan.
Rails update_Attributes(params[:user])
I have a User show function that makes an api call and updates the user attributes, but I'm having trouble saving the changes. def show #user = User.find(params[:id]) ... change attributes of #user ... if #user.update_attributes(params[:user]) p "Saved" else p "Failed" end end I'm updated two attributes (:today_steps and :year). Both are accessible in the user.rb. The challenge is that :year is a Serialized Array, but I'm not sure how that changes anything this setup has returned a 'Failed' every time. i have also tried 'params[:id]' but I receive the error undefined method `stringify_keys' for "1":String Any ideas on how to solve this? thanks more info def show #user = User.find(params[:id]) if (#user.device != nil) #device = #user.device client = Fitgem::Client.new( #api client - haven't figured out my security yet, so I won't post it ) now = Time.now() last = #device.lastUpdated while now.to_date > last.to_date day = client.activities_on_date last.strftime("%Y-%m-%d") break if day['errors'] != nil day = day['summary']['steps'] last = last + 1.days #user.year = #user.year.unshift(day) end day = (client.activities_on_date 'today') if day['errors'] == nil #user.today_steps = day['summary']['steps'] if now.day != last.day #user.year.unshift(#user.today_steps) else #user.year[0] = #user.today_steps end end #device.lastUpdated = now p #user.year #user.device.save if #user.update_attributes(params[:user]) p "Updated" else p "Failed to Update" end end #competitions = #user.competitions.paginate(page: params[:page]) end _ User.rb attr_accessible :email, :name, :password, :password_confirmation, :year, :today_steps serialize :year, Array Even More console, using params[:user] Started GET "/users/2" for 127.0.0.1 at 2014-02-25 17:15:09 -0800 Processing by UsersController#show as HTML Parameters: {"id"=>"2"} User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", "2"]] Device Load (0.1ms) SELECT "devices".* FROM "devices" WHERE "devices"."user_id" = 2 LIMIT 1 (0.1ms) begin transaction (0.3ms) UPDATE "devices" SET "lastUpdated" = '2014-02-26 01:15:09.634622', "updated_at" = '2014-02-26 01:15:10.392810' WHERE "devices"."id" = 3 (2.3ms) commit transaction (0.1ms) begin transaction User Exists (0.1ms) SELECT 1 FROM "users" WHERE (LOWER("users"."email") = LOWER('fire#fox.com') AND "users"."id" != 2) LIMIT 1 (0.0ms) rollback transaction Rendered users/show.html.erb within layouts/application (0.4ms) Rendered layouts/_shim.html.erb (0.0ms) User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = '1f47eae1cb002cb1c4e83ce0363fbfb2d33ad8f5' LIMIT 1 Rendered layouts/_header.html.erb (2.2ms) Rendered layouts/_footer.html.erb (0.1ms) console #user after find #<User id: 1, name: "Marcus", email: "mar#comcast.net", points: nil, created_at: "2014-02-25 21:59:12", updated_at: "2014-02-25 21:59:12", password_digest: "$2aGTx..FbLP...", admin: false, remember_token: "e4bdd9c", year: [], lastUpdate: nil, lastUpdated: nil, today_steps: nil>
Your code is as #user = User.find(params[:id]) ... change attributes of #user ... if #user.update_attributes(params[:user]) #the log Started GET "/users/2" for 127.0.0.1 at 2014-02-25 17:15:09 -0800 Processing by UsersController#show as HTML Parameters: {"id"=>"2"} the log clearly states that the show method receives only 'id' whereas in your controller your expect a hash 'user' which can be supplied to update_attributes. 'show' expects to get an attributes hash to stringify it's keys for the column names.