I am trying to conditionally validate full_name and zip based on whether a visitor is part of a test (Visitors that are part of the test will have certain session data). I am able to pass true/false from the leads controller to the customer model via customer.visitor_test(), but I can't access #test from in_test? in the model. What am I missing?
customer.rb
/* Stripped down code */
class Customer < ActiveRecord::Base
attr_accessor :test
validates :full_name, presence: true, if: :not_in_test?
validates :zip, presence: true, if: :in_test?
def visitor_test(bool)
#test = bool
end
def in_test?
#test
end
def not_in_test?
!self.in_test?
end
end
leads_controller.rb
/* Stripped down code */
class LeadsController < ApplicationController
def create
session[:zip] = zip
session[:email] = email
session[:full_name] = full_name
session[:email_opt_in] = email_opt_in
session[:phone] = phone
listing = Listing.where(id: listing_id).first
customer = create_or_update_customer_from_session(listing)
customer.visitor_test(/* true || false */)
if customer.errors.blank?
/* Do something */
else
/* Something else */
end
end
end
/* Stripped down code */
class Customer < ActiveRecord::Base
attr_accessor :test
validates :full_name, presence: true, if: :not_in_test?
validates :zip, presence: true, if: :in_test?
def in_test?
test
end
def not_in_test?
!in_test?
end
end
attr_accessor provides setter and getter.
/* Stripped down code */
class LeadsController < ApplicationController
def create
session[:zip] = zip
session[:email] = email
session[:full_name] = full_name
session[:email_opt_in] = email_opt_in
session[:phone] = phone
listing = Listing.where(id: listing_id).first
customer = create_or_update_customer_from_session(listing) customer.test = true
customer.save
if customer.errors.blank?
/* Do something */
else
/* Something else */
end
end
end
Related
I have 2 rails models which look like this
class Physician < UserProfile
has_many :state_licenses, inverse_of: :physician, autosave: true, dependent: :destroy
validates :state_licenses, :length => { :minimum => 1, message: "Please select at-least one state license"}
class StateLicense < ApplicationRecord
include RailsAdminPhysicianDependencyConcern
belongs_to :physician, inverse_of: :state_licenses
belongs_to :state, optional: true
attr_accessor :client_id
validates :state, presence: { message: I18n.t("errors.choose_one", field: 'state') }
#validates :license_number, presence: { message: I18n.t("errors.blank") }
def name
return "" unless state
"#{state.try(:name)}"
end
end
In my controller, I am using the code below to create a new Physician record with a bunch of state licenses but for some reason, the state licenses I pass to the create function never make it to the Physician model
def create
physician = nil
ActiveRecord::Base.transaction do
state_licenses = params["state_licenses"]
state_licenses_For_Association = []
if (state_licenses != nil)
state_licenses.each do |state_license|
sl = {}
sl[:state_id] = state_license
state_licenses_For_Association.push(sl)
end
end
physician = Physician.create(params.permit(:first_name, :last_name, :title, :residency_board_status, :residency_specialty_id, :state_licenses => state_licenses_For_Association))
user_record = nil
super do |user|
user_record = user
user.errors.delete(:user_profile)
physician.errors.messages.each { |field, messages| messages.each {|message| user.errors.add(field, message)} }
end
raise ActiveRecord::Rollback unless user_record.persisted? && physician.persisted?
end
AdminNotificationsMailer.physician_signed_up(physician).deliver_now rescue nil
end
What am I doing wrong?
Try changing this:
physician = Physician.create(params.permit(:first_name, :last_name, :title, :residency_board_status, :residency_specialty_id, :state_licenses => state_licenses_For_Association))
to this:
physician = Physician.create(params.permit(:first_name, :last_name, :title, :residency_board_status, :residency_specialty_id).merge(state_licenses: state_licenses_For_Association)) # note the .merge call
The task was allow user to view, create, edit records in different units, i.e. altitude in meters and feets, speed in m/s, knots, km/h, mi/h.
I've read a lot about Value Objects, composed_of and why we should not use it and use serialize instead and came with solution below.
It seems like complex solution for me.
Could you please point me what can I refactor and direction for it?
app/models/weather.rb
class Weather < ActiveRecord::Base
attr_accessor :altitude_unit, :wind_speed_unit
attr_reader :altitude_in_units, :wind_speed_in_units
belongs_to :weatherable, polymorphic: true
before_save :set_altitude, :set_wind_speed
validates_presence_of :actual_on, :wind_direction
validate :altitude_present?
validate :wind_speed_present?
validates_numericality_of :altitude, greater_than_or_equal_to: 0, allow_nil: true
validates_numericality_of :altitude_in_units, greater_than_or_equal_to: 0, allow_nil: true
validates_numericality_of :wind_speed, greater_than_or_equal_to: 0, allow_nil: true
validates_numericality_of :wind_speed_in_units, greater_than_or_equal_to: 0, allow_nil: true
validates_numericality_of :wind_direction, greater_than_or_equal_to: 0, less_than: 360, allow_nil: true
serialize :altitude, Distance
serialize :wind_speed, Velocity
def wind_speed_in_units=(value)
#wind_speed_in_units = value_from_param(value)
end
def altitude_in_units=(value)
#altitude_in_units = value_from_param(value)
end
private
def value_from_param(value)
return nil if value.is_a?(String) && value.empty?
value
end
def altitude_present?
return if altitude.present? || altitude_in_units.present?
errors.add :altitude, :blank
end
def wind_speed_present?
return if wind_speed.present? || wind_speed_in_units.present?
errors.add :wind_speed, :blank
end
def set_altitude
return if altitude_in_units.blank? || altitude_unit.blank?
self.altitude = Distance.new(altitude_in_units, altitude_unit)
end
def set_wind_speed
return if wind_speed_in_units.blank? || wind_speed_unit.blank?
self.wind_speed = Velocity.new(wind_speed_in_units, wind_speed_unit)
end
end
app/model/distance.rb
class Distance < DelegateClass(BigDecimal)
FT_IN_M = 3.280839895
def self.load(distance)
new(distance) unless distance.nil?
end
def self.dump(obj)
obj.dump
end
def initialize(distance, unit = 'm')
value = convert_from(BigDecimal.new(distance), unit)
super(value)
end
def dump
#delegate_dc_obj
end
def convert_to(unit)
method = "to_#{unit}"
raise ArgumentError, "Unsupported unit #{unit}" unless respond_to? method
send method
end
def convert_from(val, unit)
method = "from_#{unit}"
raise ArgumentError, "Unsupported unit #{unit}" unless respond_to? method
send method, val
end
def to_m
#delegate_dc_obj
end
def to_ft
#delegate_dc_obj * FT_IN_M
end
def from_m(val)
val
end
def from_ft(val)
val / FT_IN_M
end
end
app/models/velocity.rb is almost the same as distance.
I have a model with translated fields using the globalize gem and globalize-accessors gem for providing localized attributes such as name_en, name_zh_hk for a localized name field.
for example:
class Person < ActiveRecord::Base
translates :name
globalize_accessors: locales: [:en, :"zh-HK"], attributes: [:name]
# problem is:
validates :name, presence: true, uniqueness: true
end
So now name_en and name_zh_hk can get and set the value in corresponding locale correctly.
However, the validates :name validates only the name field in Person model. I also want to validate the uniqueness of the chinese input.
In short, would like a (easy) way to validate uniqueness of both name_en and name_zh_hk
** I have a form to submit both name_en and name_hk.
At the end of your person.rb model file (outside of class Person ... end, add this:
Person::Translation.class_eval do
validates_presence_of :name
validates_uniqueness_of :name
end
You have to do this
class Person < ActiveRecord::Base
translates :name
class Translation
validates :name, presence: true, uniqueness: true
end
end
I could be confused what you are asking about unique scopes:
validates :name, uniqueness: {scope: :blah}
specifically you may want to have a "PersonName" model.
PersonName
name | local | person_id
Person has_many :names
then have:
validates :name, uniqueness: { scope: :person_id }
this way if they enter a name for HK the same as the name for :en it will not be valid.
Solved with the following code.
Model
# /app/models/category.rb
...
I18n.available_locales.each do |locale|
validates :"name_#{locale}", presence: true, length: { maximum: 5 }, uniqueness: true
end
Validator
# config/initializers/associated_translations_uniqueness_validator.rb
require 'active_record'
require 'active_record/validations/uniqueness.rb'
ActiveRecord::Validations::UniquenessValidator.class_eval do
def validate_each_with_associated_translations(record, attribute, value)
klass = record.class
if klass.translates? && !klass.translated?(attribute) && klass.globalize_attribute_names.include?(attribute)
attribute_parts = attribute.to_s.rpartition('_')
raw_attribute = attribute_parts.first.to_sym
locale = attribute_parts.last.to_sym
finder_class = klass.translation_class
table = finder_class.arel_table
relation = build_relation(finder_class, table, raw_attribute, value).and(table[:locale].eq(locale))
relation = relation.and(table[klass.reflect_on_association(:translations).foreign_key].not_eq(record.send(:id))) if record.persisted?
translated_scopes = Array(options[:scope]) & klass.translated_attribute_names
untranslated_scopes = Array(options[:scope]) - translated_scopes
untranslated_scopes.each do |scope_item|
scope_value = record.send(scope_item)
reflection = klass.reflect_on_association(scope_item)
if reflection
scope_value = record.send(reflection.foreign_key)
scope_item = reflection.foreign_key
end
relation = relation.and(find_finder_class_for(record).arel_table[scope_item].eq(scope_value))
end
translated_scopes.each do |scope_item|
scope_value = record.send(scope_item)
relation = relation.and(table[scope_item].eq(scope_value))
end
if klass.unscoped.with_translations.where(relation).exists?
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
end
else
validate_each_without_associated_translations(record, attribute, value)
end
end
alias_method_chain :validate_each, :associated_translations
end
I'm getting this error in Rails and there's no other information to help me figure out why.
SystemStackError in UserController#students
stack level too deep
My user controller is as follows
class UserController < ApplicationController
before_filter :authenticate_user!, :except => [:show, :students, :mentors]
...
def students
#users = Student.where(:verify_code => 'VERIFIED')
end
end
Even when I remove the #users line the error persists.
EDIT: The error seems to happen to every single route aside from the devise_for :users and to_root, which means just the User controller.
User Model
require 'json'
require 'socket'
class User < ActiveRecord::Base
serialize :roles
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :ign, :server, :roles, :presence => true
validate :summoner_exists
validate :valid_roles
attr_accessible :email, :password, :password_confirmation, :remember_me, :ign, :server, :tier, :roles, :type
def valid_roles
valid_role_array = ['Top', 'Mid', 'Jungle', 'AD Carry', 'Support']
self.roles.each do |role|
unless valid_role_array.include?(role)
self.roles.delete(role)
end
end
end
def summoner_verified?
return self.verify_code == 'VERIFIED'
end
def summoner_verify
rune_pages = shurima_api(self.server, 'rune_pages', self.acctid)
unless rune_pages
return false
else
rune_pages.each do |page|
if (page['name'] == self.verify_code)
self.verify_code = 'VERIFIED'
self.save
return true
end
end
end
return false
end
def summoner_exists
json = shurima_api(self.server, 'summoner', self.ign)
unless json
errors.add(:ign, "The summoner name \"#{self.ign}\" doesn't exist on #{self.server}")
else
self.summonerid = json['summonerId']
self.acctid = json['acctId']
self.verify_code = Array.new(10){rand(36).to_s(36)}.join
eligible_to_mentor
end
end
def eligible_to_mentor
leagues = shurima_api(self.server, 'leagues', self.summonerid)
unless leagues
errors.add(:ign, "That summoner doesn't seem to meet the requirements to become a mentor. Make sure you're at least in a Platinum League")
return false
end
leagues.each do |league|
if (league['queue'] == 'RANKED_SOLO_5x5')
self.tier = league['tier']
end
end
eligible_tiers = ['PLATINUM', 'DIAMOND', 'CHALLENGER']
if (self.type == 'Mentor' && !eligible_tiers.include?(self.tier))
errors.add(:mentor, "Mentors must be at least PLATINUM.")
end
end
def shurima_api(server, method, args)
host, port = 'ip removed', 714
TCPSocket.open(host, port) do |socket|
ready = IO.select([socket], [socket], nil, 8)
return false unless ready
socket.puts server + "&" + method + "&" + args.to_s
message = socket.gets.chomp
if message == '"Unknown error"'
return false
end
return JSON.parse(message)
end
end
end
I ended up fixing it by renaming the request method to request_mentor because it was causing the infinite loop.
theres an excerpt of my code:
module Configuracao
extend self
class Key
include ActiveModel::Validations
attr_accessor :name, :type, :default, :validations, :group, :available_values
def initialize(params)
params.symbolize_keys!.assert_valid_keys(:name, :type, :default, :validations, :group, :available_values)
#group = params[:group]
#name = params[:name]
#type = params[:type]
#available_values = params[:available_values]
#default = params[:default]
#validations = params[:validations]
#in this way each validation is being added for all keys
Configuracao::Key.class_eval do
validates :value, params[:validations]
end
end
end
end
so for every instance key i will have a diferent validation passed in a hash, example:
Key.new( validations: { presence: true, numericality: true } )
Key.new( validations: { length: { maximum: 30 } } )
There's a way to do it?
well i found a solution, maybe not so elegant or best way to do, but it works
def initialize(params)
params.symbolize_keys!.assert_valid_keys(:name, :type, :default, :validations, :group, :available_values)
#group = params[:group]
#name = params[:name]
#type = params[:type]
#available_values = params[:available_values]
#default = params[:default]
##current_validations = nil
##current_validations = #validations = params[:validations]
class << self
validates :value, ##current_validations unless ##current_validations.blank?
end
end
now each time i instantiate a Key, the class will be modified only for that instance
Will this work?
...
validates :all_hash_validations_pass
...
def all_hash_validations_pass
...iterate through the hash here, and validate each of them
end
If not, you should be able to use a custom validator for more control.