Rails adds DB column name to error message - activerecord

I am trying to show different message than predefined one. And in my model file i added this line to check if that company exists in db.
I am using Rails 5.2.2
class Company < ApplicationRecord
validates :tax_no, :uniqueness => {message: "This tax number has already been used"}
end
And i got the result as :
Company tax no This tax number has already been used
How can i remove that "Company tax no" ?

This depends how you display your error message.
You will get proper idea from below,
u = User.new
u.valid?
# => false
u.errors.messages
# {:email=>["This field is required.", "This field is required.", "This field is required."], :password=>["This field is required."]}
u.errors.full_messages
# => ["Email This field is required.", "Password This field is required."]
u.errors.messages[:email]
# => "This field is required."
You have to inspect your view part and use #company.errors.messages[:tax_no] if #company.valid? is false for tax_no

Related

Authorize.net: How does the user specify the default payment method? How am I to guess?

When the user edits their payment methods on the hosted payment profile manage page, they are able to enter multiple payment methods and credit cards. There is no way to select a default.
When I go to get the payment profile right before a charge and leave out the payment profile id to get the default, it gives an error.
https://developer.authorize.net/api/reference/index.html#customer-profiles-get-customer-payment-profile
Note: If the payment profile has previously been set as the default payment profile, you can submit this request using customerProfileId as the only parameter. Submitting this request with only the customer profile ID will cause the information for the default payment profile to be returned if a default payment profile has been previously designated. If no payment profile has been designated as the default payment profile, failing to specify a payment profile will result in an error.
irb(main):016:0> request = GetCustomerPaymentProfileRequest.new
irb(main):017:0> request.customerProfileId = #subscription.authorizenet.customer_profile_id
=> "1503823608"
irb(main):018:0> response = transaction.get_customer_payment_profile(request)
irb(main):019:0> response.messages.resultCode
=> "Error"
irb(main):020:0> response.messages.messages[0].text
=> "No default payment/shipping profile found."
So how do I get the default payment profile id? Yes, I could get all the payment profiles, but still, how would I know which one the customer intended to use?
It's not possible. You are supposed to display another form for the user to select the default. Completely retarded. Makes me want to just violate PCI compliance and just store all the credit cards myself.
https://community.developer.authorize.net/t5/Integration-and-Testing/Get-Accept-Customer-Profile-Page/m-p/59069/highlight/true#M33670
There is no way for the customer to indicate on the form which profile they would like as the default. However, you can accomplish this by using the API to do a getCustomerPaymentProfileListRequest to retrieve [all of the] payment profiles, and then presenting that list to the customer in a way that the customer can indicate to you which one should be the default. Then, you'd do an updateCustomerPaymentProfileRequest to set the chosen profile as the default.
I ended up just allowing one payment profile. If the payment profile id doesn't exist, then it displays the ADD form. If it does, then it displays the EDIT form. The only disadvantage is it doesn't allow the user to switch between credit card and bank account after they've created the payment profile.
routes.rb
resource :payment_profile, only: [:edit] do # Authorize.net customer profile & payment profile
collection do
get :anedit
post :charge
end
end
payment_profiles_controller.rb
class PaymentProfilesController < ApplicationController
include AuthorizeNet::API
before_action :set_subscription
layout false, only: :anedit
def edit
unless #subscription.authorizenet
create_empty_customer_profile
else
get_customer_profile # update profile as they may have added a payment profile (or not)
end
end
def anedit
unless #subscription.authorizenet # in case #edit failed to create a new empty profile
render plain: "No customer profile available to edit. Please try again later." and return
end
#payment_profile_id = #subscription.authorizenet.customer_payment_profile_id
if Rails.env == 'production'
if #payment_profile_id
#action_url = "https://accept.authorize.net/customer/editPayment"
else
#action_url = "https://accept.authorize.net/customer/addPayment"
end
else
if #payment_profile_id
#action_url = "https://test.authorize.net/customer/editPayment"
else
#action_url = "https://test.authorize.net/customer/addPayment"
end
end
# show hosted form
...
setting5 = SettingType.new
setting5.settingName = SettingNameEnum::HostedProfileBillingAddressRequired
setting5.settingValue = true
settings = Settings.new([setting1, setting2, setting3, setting4, setting5, setting6])
request = GetHostedProfilePageRequest.new
request.customerProfileId = #subscription.authorizenet.customer_profile_id
request.hostedProfileSettings = settings
response = transaction.get_hosted_profile_page(request)
if response.messages.resultCode == MessageTypeEnum::Ok
# puts "Successfully got Accept Customer page token."
# puts " Response code: #{response.messages.messages[0].code}"
# puts " Response message: #{response.messages.messages[0].text}"
# puts " Token: #{response.token}"
#token = response.token
else
# puts "#{response.messages.messages[0].code}"
# puts "#{response.messages.messages[0].text}"
render plain: "Failed to get hosted profile page with customer profile ID #{request.customerProfileId}: #{response.messages.messages[0].code} #{response.messages.messages[0].text}"
return
end
end
# create authorize.net customer profile
def create_empty_customer_profile
# Build the request object
request = CreateCustomerProfileRequest.new
# Build the profile object containing the main information about the customer profile
request.profile = CustomerProfileType.new
request.profile.merchantCustomerId = #user.id
request.profile.email = #user.email
response = transaction.create_customer_profile(request)
if response != nil
puts response.messages.resultCode
if response.messages.resultCode == MessageTypeEnum::Ok
puts "Successfully created a customer profile with id: #{response.customerProfileId}"
puts " Customer Payment Profile Id List:"
response.customerPaymentProfileIdList.numericString.each do |id|
puts " #{id}"
end
puts " Customer Shipping Address Id List:"
response.customerShippingAddressIdList.numericString.each do |id|
puts " #{id}"
end
#subscription.create_authorizenet user: #user, customer_profile_id: response.customerProfileId #, customer_payment_profile_id: response.customerPaymentProfileIdList.numericString.first
else
puts response.messages.messages[0].code
puts response.messages.messages[0].text
flash.now.alert = "Failed to create a new customer profile: #{response.messages.messages[0].code} #{response.messages.messages[0].text}"
#render :new
end
else
puts "Response is null"
flash.now.alert = "Failed to create a new customer profile."
end
end
def get_customer_profile
request = GetCustomerProfileRequest.new
request.customerProfileId = #subscription.authorizenet.customer_profile_id
response = transaction.get_customer_profile(request)
if response.messages.resultCode == MessageTypeEnum::Ok
puts "Successfully retrieved customer profile of customer ID #{request.customerProfileId}."
response.profile.paymentProfiles.each do |paymentProfile|
puts " Payment Profile ID #{paymentProfile.customerPaymentProfileId}"
puts " Payment Details:"
if paymentProfile.billTo != nil
puts " Last Name: #{paymentProfile.billTo.lastName}"
puts " Address: #{paymentProfile.billTo.address}"
end
end
if response.subscriptionIds != nil && response.subscriptionIds.subscriptionId != nil
puts " List of subscriptions: "
response.subscriptionIds.subscriptionId.each do |subscriptionId|
puts " #{subscriptionId}"
end
end
# now update the payment profile id
#subscription.authorizenet.update customer_payment_profile_id: response.profile.paymentProfiles.first&.customerPaymentProfileId
else
puts response.messages.messages[0].text
flash.alert = "Failed to get profile of customer ID #{request.customerProfileId}."
end
return response
end
And the views
edit.haml
-content_for :head do
%meta(name="turbolinks-cache-control" content="no-preview")
%iframe{src: anedit_user_subscription_payment_profile_path(#user), width: '100%', height: '900px', frameborder: 0}
anedit.haml
%form#authorizenetform{:action => #action_url, :method => "post"}
%input{:type => "hidden", :name => "token", :value => #token}/
-if #payment_profile_id
%input{:type => "hidden", :name => "paymentProfileId", :value => #payment_profile_id}/
%input{:type => "submit", :value => "Update Payment"}/
-# Separate iframe document without layout. No access to JQuery.
:javascript
document.getElementById('authorizenetform').submit();

Rails 4 validation message: removes "_id" from message

# model.rb
validates :employee_id, presence: true, uniqueness: true
When left empty, the error message says "Employee can't be blank" when I want it to say "Employee ID can't be blank".
I resolved this by:
# model.rb
validates :employee_id, presence: { message: " ID can't be blank" }, uniqueness: true
which outputs "Employee ID can' be blank".
However, this isn't a really good solution IMO. I would like some means of customizing the entire message, including the attribute prefix.
Is there a simple way to do this?
There are several "correct" ways to go about this, but you definitely shouldn't do it via the validation itself, or by defining your own validation method.
On a model-by-model level, this is controlled by the class-level human_attribute_name method.
If you want your model's employee_id field to be a special case where the _id postfix isn't truncated, define that special case by overridding human_attribute_name:
class MyModel
validates :employee_id, presence: true
def self.human_attribute_name(attr, options = {})
attr == :employee_id ? 'Employee ID' : super
end
end
In broader terms, you can redefine human_attribute_name on ActiveRecord::Base to override handling of all _id attributes, but I doubt you want to do this. Generally, it's a good thing that Rails drops the _id postfix.
The second (and probably better) mechanism is to simply rely on localization. ActiveRecord ties into your locale YAML files for just about everything. If you want your employee_id field to humanize to Employee ID regardless of language, you'll need to edit your YAML files.
# config/locales/en.yml
en:
activerecord:
attributes:
employee_id: "Employee ID"
You can override human_attribute_name and always send default value with id
class MyModel
def self.human_attribute_name(attribute, options = {})
super(attribute, { default: attribute.to_s.humanize(keep_id_suffix: true) } )
end
end
You can write a custom validation. By doing it that way, you get to define the error message inside the validation method.
The relevant section from Rails Guides is here: Performing Custom Validations
Something like:
Class Paystub
validate :employee_id_is_not_blank
def employee_id_is_not_blank
errors[:base] << "Employee must be a part of the record.") if id.blank?
end
end
p = Paystub.create
p.errors.full_messages #=> ["Employee must be a part of the record."]
Section 7.4 in the Rails Guides specified using errors[:base]. Error messages shoveled into :base don't require an attribute to be tied to them.
Update: This is not the right answer. See #meagars answer above.

Using ActiveRecord with column names with spaces in them

I work on a team that is using ActiveRecord to access the schema on a MSSQL server. Modifying the schema is not an option and the column names have spaces in them, a la SPACEY COLUMN.
When writing the ActiveRecord class to access a table with spaces in it, what is good practice?
We also need this to work with factory girl...
AR-JDBC (as well as the AR-SQLServer-Adapter) will/should handle this just fine since it auto-magically quotes column name identifiers using "[ COlumn NAME ]" ... I personally would hide this from bubbling up as much as possible e.g. using aliases :
class MySpacey < ActiveRecord::Base
set_table_name 'SPACEY TABLE'
set_primary_key 'MY ID'
alias_attribute :id, :'MY ID'
end
Consider User is the Model and User Name is the column you need to access.
User.where('User Name' => 'Bob')
You can add multiple conditions also,
User.where('User Name' => 'Bob', 'Email Address' => 'sample#sample.com')
You can also try,
User.where('[User name] = ? AND [Email Address] = ?', 'Bob', 'sample#sample.com')
And if your table itself has space in it. Try,
class User < ActiveRecord::Base
self.table_name = "user table"
end

Rails 3 - Validation for Time existence

I have a model with a Time attribute. I want to check that time can not be empty (better choice probably would be to check that input is time but i have no ideas how to deal with that). I tried this validation:
# id :integer not null, primary key
# school_class_id :integer
# meeting_time :time
class Meeting < ActiveRecord::Base
validates :meeting_time,
:presence => { :message => "can't be empty!" }
end
Then i tried to check this in spec and this fails (empty time is ok but it should not be). What do i do wrong?
Rspec:
# id :integer not null, primary key
# school_class_id :integer
# meeting_time :time
require 'spec_helper'
describe Meeting do
before(:each) do
#class = FactoryGirl.create( :school_class )
#attr_meeting = {
:meeting_theme => 'Text is here',
:meeting_date => "#{Date.today}",
:meeting_time => "#{Time.now}",
:meeting_room => '1234'
}
end
describe "Validations" do
describe "Rejection" do
it "should reject blank time" do
wrong_attr = #attr_meeting.merge( :meeting_time => " " )
#class.meetings.build( wrong_attr ).should_not be_valid
end
end
end
end
Error:
Meeting Validations Rejection should reject blank time
Failure/Error: #class.meetings.build( wrong_attr ).should_not be_valid
expected valid? to return false, got true
In your example, you assign " " to the meeting_time, not nil or "". But Time object somehow can be successfuly generated from non-empty, even blank string. Setting meeting_time to "" or nil should solve yout problem.
Maybe I don't fully understand something, but I think it's not very logical and predictable behaviour. Need to take a look into the sources.

Sinatra and Datamapper not able to save an array to the datatype Object

I have an HTML form which uses the following Sinatra code to handle POST for the url '/add-artist':
post '/add-artist' do
if logged_in?
a = Artist.new
a.name = params[:name]
a.website = params[:website]
a.facebook = params[:facebook]
a.created_by = session[:user_id]
a.created_at = Time.now
a.updated_by = session[:user_id]
a_updated_at = Time.now
a.views = 0
a.save
#user = User.get session[:user_id]
#user.artists.push(a.id)
#user.save
redirect '/'
end
end
The object 'a' is being saved but '#user' is not. I guess more specifically, the value '#user.artists' is not being updated. If you need more info, please ask but I have a feeling that you Ruby vets will find the problem in the code I provided.
UPDATE
Here's some additional info. I was able to reproduce the error in irb. First here's my class definition for 'User'.
# dm_models.rb
require 'data_mapper'
DataMapper::setup(:default, "sqlite3://#{Dir.pwd}/event_review.db")
class User
include DataMapper::Resource
property :id, Serial
property :email, String
property :password, String
property :user_name, String
property :birthdate, Date
property :city, String
property :state, String
property :zip, String
property :geodata, Object
property :bio, Text
property :friends, Object
property :events, Object
property :event_reviews, Integer
property :artists, Object
property :artist_reviews, Integer
property :venues, Object
property :venue_reviews, Integer
property :created_at, DateTime
property :updated_at, DateTime
property :views, Integer
has n, :reviews
end
Here is the irb
>> require 'sinatra'
=> true
>> require 'data_mapper'
=> true
>> require './models/dm_models.rb'
=> true
>> require 'geokit'
=> true
>>
?> include Geokit::Geocoders
=> Object
>> u = User.get 8
=> #<User #id=8 #email="km#km.com" #password="km" #user_name="Katherine Miller" #birthdate=#<Date: 4895485/2,0,2299161> #city="Burbank" #state="CA" #zip="91501" #geodata=#<Geokit::GeoLoc:0x10150d4d8 #street_number=nil, #suggested_bounds=#<Geokit::Bounds:0x10150cf88 #sw=#<Geokit::LatLng:0x10150cd80 #lng=-118.315043, #lat=34.1766949>, #ne=#<Geokit::LatLng:0x10150cee8 #lng=-118.27996, #lat=34.221666>>, #lng=-118.2935891, #zip="91501", #state="CA", #precision="zip", #province=nil, #all=[#<Geokit::GeoLoc:0x10150d4d8 ...>], #street_address=nil, #provider="google", #city="Burbank", #lat=34.2039087, #country_code="US", #full_address="Burbank, CA 91501, USA", #street_name=nil, #accuracy=5, #country="USA", #success=true> #bio=<not loaded> #friends=[] #events=["13", "14", "15", "16", "28", "29"] #event_reviews=7 #artists=[] #artist_reviews=1 #venues=[] #venue_reviews=0 #created_at=#<DateTime: 70729968253/28800,-5/24,2299161> #updated_at=#<DateTime: 1178838019/480,-5/24,2299161> #views=56>
>>
?> u.artists
=> []
>> u.artists.push "5"
=> ["5"]
>> u.save
=> true
>> u = User.get 8
=> #<User #id=8 #email="km#km.com" #password="km" #user_name="Katherine Miller" #birthdate=#<Date: 4895485/2,0,2299161> #city="Burbank" #state="CA" #zip="91501" #geodata=#<Geokit::GeoLoc:0x1014e8638 #street_number=nil, #suggested_bounds=#<Geokit::Bounds:0x1014e80e8 #sw=#<Geokit::LatLng:0x1014e7eb8 #lng=-118.315043, #lat=34.1766949>, #ne=#<Geokit::LatLng:0x1014e8048 #lng=-118.27996, #lat=34.221666>>, #lng=-118.2935891, #zip="91501", #state="CA", #precision="zip", #province=nil, #all=[#<Geokit::GeoLoc:0x1014e8638 ...>], #street_address=nil, #provider="google", #city="Burbank", #lat=34.2039087, #country_code="US", #full_address="Burbank, CA 91501, USA", #street_name=nil, #accuracy=5, #country="USA", #success=true> #bio=<not loaded> #friends=[] #events=["13", "14", "15", "16", "28", "29"] #event_reviews=7 #artists=[] #artist_reviews=1 #venues=[] #venue_reviews=0 #created_at=#<DateTime: 70729968253/28800,-5/24,2299161> #updated_at=#<DateTime: 1178838019/480,-5/24,2299161> #views=56>
>> u.artists
=> []
>> u.artists.class
=> Array
The description of the above code: I retrieve user with id==8, push the value "5" into it. This appears to be successful. I save user#8. Then I re-retrieve user#8 and look at the artists value and it is an empty array.
And finally, I am able to update other fields like "artist_reviews". Is this because I am defining the datatype to be 'Object' for artists, events and venues? This problem exists for all of those fields.
Thanks for the help.
What do the logs say? Can you push to #user.artists? Is it an Array? It might fail validation and you cannot complete save.
I asked this a while ago but I'm pretty certain the solution is to serialize the object. In this case it was an array of integers. I'm not sure if I'll have the time to update this with a detailed solution but an array cannot be stored directly in a relational database. The array object must be 'serialized', essentially converted to a string. In this example the datatype for the artist attribute would then be text.
You could manually convert the artist column to an array (from string) push the new integer(s) into the array and then convert back to string and save. I'm assuming there is an automated way to do this but that's the idea.
Furthermore, this entire example is a poor way to handle associations. The better way to do this is to have a one-to-many association where there is an ArtistUser table which has two columns artist_id and user_id. Every new association is represented as a new row in the ArtistUser table.

Resources