Set column value based on email address regex? - ruby-on-rails

My Rails application is targeted at college students -- only Berkeley and University of Washington students can sign up right now, and I use a regex to do it. The user model has a column in it called "school" that I'd like to fill with the name of their school based on their email address. Here's an example of what the validation might look like in my User.rb file (with just Berkeley):
berkeley_regex = /\A[\w+\-.]+#berkeley\.edu\z/i
validates :email, :presence => true, :format => {:with => berkeley_regex}
In the User controller, how could I go about setting the #user.school column to be Berkeley, for example, or University of Washington if I add a regex to recognize that email domain?

Move the regex matching into a method.
You could associate the regex with a key in a hash, too, making this easier, roughly:
EMAILS = {
"Berkeley": /\A[\w+-.]+#berkeley.edu\z/i,
"Washington": /whatever/
}
Iterate over the hash to get the value and regex:
EMAILS.each do |school, regex|
# If it matches, return the `school` value
end
Use the same method for your email validation by validating :email against the method instead of a simple regex as detailed in the Custom Validation section of the Active Record Validations Guide. You may need to tweak the return value etc. to get the function to work for both things, or write a thin wrapper for the validation around the lookup.
Consider a callback to actually set the value, e.g., :after_validation or :before_save.

First, create a new method that returns Berkeley or U of Washington depending on which regex matches.
Next, you will want to do an update on the user so:
user_school = find_what_school_user_goes_to(#user.email)
#user.update(:school user_school)

Related

validation on uniqueness record on the association relation

I have some query, like a :
Assignment.where(date: "2019-07-01")
.includes(crews: :truck)
.where.not({ crews: {truck_id: nil}})
.count
#=> 2 records.
Here's an example with contain:
Assignment.where(date: "2019-07-01")
.includes(crews: :truck)
.where.not({ crews: {truck_id: nil}})
.distinct.pluck('trucks.name')
#=> ["Mercedes", "BMW"]
I want to check for the uniqueness of truck_id in Assignment.where(date: "2019-07-01")
And in order to attach an existing truck_id to another Assignment object, validation was triggered.
And a message pops up that you cannot add, since such a Truck object is already attached to one of the Assignment on the day that is specified in .where
Please tell me how to implement it correctly. Thank.
If you don't want to write a validate method, you can still use uniqueness options.Use conditional option to tell which rows should do the validation.
validates :truck_id, uniqueness: true, if: Proc.new { |assignment| assignment.on_specified_day? }

Field must only contain letters. How to do this with Rails ActiveRecord Validation?

I'm working with a rails app and the project required the firstname and lastname fields of the User model to only accept letters.
How do you do this in Rails ActiveRecord validation?
Just to expand on the conversation in the comments that's without an answer attached, the following should be what you're after:
LETTER_REGEX = /\A\p{L}+\Z/.freeze
validates :lastname, format: { with: LETTER_REGEX, message: "only allows letters" }
I'd recommend Regex101 if you're trialling different approaches. It lets you test input, and provides a good explanation of the regex. Here, for this example:
\A asserts position at start of the string
\p{L}+
matches any kind of letter from any language
+ Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
\z asserts position at the end of the string
Re \p, in this case it's taking a Unicode variant (all of which can be viewed here, and in far more detail here).
Hope that's useful - let me know how you get on or if you have any questions.
RTM: https://guides.rubyonrails.org/active_record_validations.html
Will be something like this
class User < ApplicationRecord
LETTER_ONLY_RE = /\A\p{Letter}+\z/
validates :lastname, format: { with: LETTER_ONLY_RE,
message: "only allows lowercase letters" }
# same for firstname
end

Use Ruby ActiveRecord Validates_Numericality to accept 20.00 as integer

We are working with web development firm to build a site. We have a field where we request that the user input the amount that they would like to invest. We asked for this field to be limited to whole numbers. For example, the user should be able to invest "20" or "20.00" but not "20.50".
The developer is using Ruby ActiveRecord Validate_numericality :only_integer which is restricting the input to "20". If a user inputs and submits the value "20.00" they receive an error telling them that they need to input an integer.
Is there a way to use ActiveRecrod validate_numericality to accept only numbers that are whole numbers, not necessarily only integers? The code is currently:
validates :principal, numericality: {greater_than_or_equal_to:MINIMUM_INVESTMENT_AMOUNT,
less_than_or_equal_to:MAXIMUM_INVESTMENT_AMOUNT,
:only_integer => true}
I am hoping that there is a numericality constraint that will allow 20.00 as a whole number.
you could do this
before_validation {|a| a.principal = principal.to_i}
This will convert it to an integer without modifying the validations but if you want to notify the user that they entered something that is not a whole number then I would remove the :only_integer and create a more flexible validation like
validates :principal, numericality:{greater_than_or_equal_to:MINIMUM_INVESTMENT_AMOUNT, less_than_or_equal_to:MAXIMUM_INVESTMENT_AMOUNT}
validate :principal_is_whole_number
def principal_is_whole_number
errors.add(:principal, "must be a whole number.") unless principal.to_i == principal
end

Updating Attribute through reference

I am wondering if it's possible to reference an object's attribute.
The object User have attribute first_name, so normally if we want to update the first name attribute we do:
user0 = User.new()
user0.update_attribute(:first_name, "joe")
now my question is can I update the :first_name attribute through another variable/symbol like so:
user0.update_attribute(:fname_ref, "jack")
user0.first_name #=> has the value jack
I was looking for variable reference like in Perl, but came up nothing in Ruby.
---------- EDIT
I am in the middle of doing the lynda ruby on rails tutorial, and in the middle of creating a module to adjust positions of items in a table.
unfortunately when I first started I named my tables columns differently
pages.page_position, subjects.subject_position, sections.section_position
now the module PositionMover is to be used accross three models, so now
I have a problem since the attributes names are different for each model
so I thought no worry I'll just create a pointer / refference for each model
:position = :pages_position , :position = :subjects_position , :position = :section_position
hence the question , if its even possible to do it.
if its not possible , any suggestion what should I do , so the module can
be used accross three different models , with different attribute names.
sorry I am a newbie.
Symbols are not like variables, they are actually a object type, like String or Fixnum so you can have a variable that is of type symbol.
I think this is what you are looking for:
attribute = :first_name
user0.update_attribute(attribute, "jack")
user0.first_name #=> has the value jack
Update: If you have a String and need to convert to a symbol, (I'm not sure if you need this for update_attribute)
foo = "string"
foo.to_sym #=> :string
Use the alias_attribute . Define into each model like :
Page model
alias_attribute :position , :pages_position
Subject Model
alias_attribute :position , :subjects_position
Section Model
alias_attribute :position , :section_position
And use (Model.position = values) with each model . Hope Its solution of your problem .
You can also use send docs and use symbols or strings to reference the attribute's methods. send can be incredibly useful since it enables you to choose the method that you'll be invoking at runtime.
Eg
user.first_name = "Jack" # set first_name to Jack
# Note: method "first_name=" (a settor) is being used
attribute = "first_name"
user.send attribute + "=", "Jack" # set first_name to Jack
# The string "first_name=" is coerced into
# a symbol
attribute = :first_name
val = user.send attribute # => returns "Jack"
see the definition of update_attribute in the ActiveRecord::Persistence module on github:
def update_attribute(name, value)
name = name.to_s
verify_readonly_attribute(name)
send("#{name}=", value)
save(:validate => false)
end
this leads me to believe you could add the following to your model to achieve that behavior:
alias_method :fname_ref= :first_name=
I'd be interested to know why you want to do that as #Andrew Marshall asked.

Where should I store a list of select options in Rails 3?

Using Rails 3.1.3 and Ruby 1.9.3.
I want to give the user a list of possible date/time formats. The user's selection will be stored in the Users table. Date/time values are then formatted using the I18n.localize function. I actually have 10 formats; here by way of example are the first two:
config/locales/datetime.en.yml
en:
time:
format_labels:
mdyslash12: mm/dd/yyyy - hh:mm am (12-hour)
mdyslash24: mm/dd/yyyy - hh:mm (24-hour)
formats:
mdyslash12: ! '%m/%d/%Y %I:%M%p'
mdyslash24: ! '%m/%d/%Y %H:%M'
My question is where to store the list of possible date/time formats. I've identified three possibilities.
1. List options as a CONSTANT in model:
app/models/user.rb
DATETIME_FORMATS = %w[mdyslash12 mdyslash24]
validates :datetime_format, :presence => true,
:inclusion => { :in => DATETIME_FORMATS }
2. Create an application constant and validate against that:
config/initializers/constants.rb
Rails.configuration.datetime_formats = "mdyslash12 mdyslash24"
app/models/user.rb
validates :datetime_format, :presence => true,
:inclusion => { :in => Rails.application.config.datetime_formats.split(" ") }
3. Validate directly against the locale file:
app/models/user.rb
validates :datetime_format, :presence => true,
:inclusion => { :in => (I18n.t 'time.format_labels').stringify_keys.keys }
This option uses a feature that is new to me: I18n.t 'time.format_labels' returns a hash of ALL keys and values from that branch of the locale file. The hash keys are symbols, so to get a string array, I call stringify_keys to convert the symbols to strings, then keys to give me only the keys (no values).
Option #3 is the DRYest in that I don't have to list the possible values in two places. But it doesn't feel quite right to depend on the locale file for the discreet list of possible date/time formats.
What would you recommend? One of these options? Something else?
I'd go with option 1 to start with, since it's simple, clear, and fairly DRY. I might refactor to option 2 if I ended up needing that constant in another model.
Option 3 has the potential to behave differently based on the locale, so I don't like that. If you end up forgetting to specify your format labels in a new locale, your selection list might end up being empty (or if there's a typo in one locale, it might take longer to notice, since the typo would be treated as valid for that locale). Regardless it's probably a good idea to unittest this in all your supported locales.

Resources