Replace space with dash before save using Rails 3 - ruby-on-rails-3

I am trying to save a name to the database and a single word (firstname) works fine but when the user enter both firstname and lastname I want Rails to save it to the database as firstname-lastname instead of firstname lastname (space between).
I know I perhaps should use a before create filter but I am not sure how this need to look like. I want the validation to work to, i.e. no two people should be able to use the same name.
I am using Rails 3.

You can use ActiveSupport's inflector method parameterize on the string.
name = 'john smith' # => john smith
name.parameterize # => john-smith
Further, parameterize takes an option to use for the word-break, so you can replace the dash with an underscore like this:
name.parameterize("_") # => john_smith
An advantage of using parameterize is that it normalizes the characters to the latin, so...
name = "jöhanne såltveç"
name.parameterize # => johanne-saltvec
EDIT: As of Rails 5.0.0.1 the separator needs to be passed as an option. Therefore: name.parameterize(separator: '_')

Why don't you just have first_name and last_name columns in the db, and create your own validation rule to make sure the combination is unique (http://guides.rubyonrails.org/active_record_validations_callbacks.html#creating-custom-validation-methods). You should also create a unique index over those two columns in your db.

Another option would be to us regexp and replace all existing spaces with. You'd put something along the lines of:
self.firstname.gsub(/\s+/, '-')
in your model.
Note: I'm not sure if ruby accepts \s as "any whitespace character" And I think the * should make sure that if someone enters a name with two neighbour spaces(or more) it won't convert each space into a separate dash, but only into one.

Other answer is correct,
But, if you want to preserve case while parameterizing
name = "Donald Duck"
name.parameterize(preserve_case: true) # => Donald-Duck

Related

filter params for import users from AD

I'm to import users used this filter:
(&(objectClass=user)(objectCategory=PERSON))
And i want to add RealName parameter as filter.
RealName should contain 3 any words.
For example RealName contained "name middle_name surname" - it's good, need to import.
If RealName contained "name surname" (only two word) - wrong, not imported.
Can you help me with with filter?
LDAP queries can only use attributes that exist in Active Directory, and there is no attribute called "RealName".
You will have to split the input string yourself. So, for example, if you were given the string "Necro The Human", you would have to split that into 3 strings using whatever programming language you're using.
Then you will have to insert those into an LDAP query that matches the three name attributes: givenName, initials, and sn (surname)
Your finished query would look something like this:
(&(objectClass=user)(objectCategory=person)(givenName=Necro)(initials=The)(sn=Human))
Check if you're using initials or the middleName attribute for the middle name. It's the initials attribute that is labelled as "Initials" in Active Directory Users and Computers, so that may be what's used, even though the documentation says it's just for the initials of the full name, or middle initials (not the full middle name). It's also limited to only 6 characters, so you may be using middleName if you're storing full middle names.
If your company has the standard of setting the displayName to the user's full name, including middle name, then you could just match against that. But I think it would be pretty rare that the middle name would be in the display name.
(&(objectClass=user)(objectCategory=person)(displayName=Necro The Human))
There is also ambiguous name resolution, but it searches other attributes (not just the first/last name) and it does not include initials or middleName. I mention it only because it's not well known and you may find some other use for it one day.

SQL wildcards via Ruby

I am trying to use a wildcard or regular expression to give some leeway with user input in retrieving information from a database in a simple library catalog program, written in Ruby.
The code in question (which currently works if there is an exact match):
puts "Enter the title of the book"
title = gets.chomp
book = $db.execute("SELECT * FROM books WHERE title LIKE ?", title).first
puts %Q{Title:#{book['title']}
Author:#{book['auth_first']} #{book['auth_last']}
Country:#{book['country']}}
I am using SQLite 3. In the SQLite terminal I can enter:
SELECT * FROM books WHERE title LIKE 'Moby%'
or
SELECT * FROM books WHERE title LIKE "Moby%"
and get (assuming there's a proper entry):
Title: Moby-Dick
Author: Herman Melville
Country: USA
I can't figure out any corresponding way of doing this in my Ruby program.
Is it not possible to use the SQL % wildcard character in this context? If so, do I need to use a Ruby regular expression here? What is a good way of handling this?
(Even putting the ? in single quotes ('?') will cause it to no longer work in the program.)
Any help is greatly appreciated.
(Note: I am essentially just trying to modify the sample code from chapter 9 of Beginning Ruby (Peter Cooper).)
The pattern you give to SQL's LIKE is just a string with optional pattern characters. That means that you can build the pattern in Ruby:
$db.execute("SELECT * FROM books WHERE title LIKE ?", "%#{title}%")
or do the string work in SQL:
$db.execute("SELECT * FROM books WHERE title LIKE '%' || ? || '%'", title)
Note that the case sensitivity of LIKE is database dependent but SQLite's is case insensitive so you don't have to worry about that until you try to switch database. Different databases have different ways of dealing with this, some have a case insensitive LIKE, some have a separate ILIKE case insensitive version of LIKE, and some make you normalize the case yourself.

Propel, Having fields with spaces

Is it possible to have a space in the field name that is defined in the schema.xml?
The bright spark who design the database, used field names like 'POST CODE' and 'CURRENT'
Is there a way around this? or am i stuck?
The database is an existing MSSQL 2005 database, with an unknown
number of systems connected to it. So it cannot be changed.
I am using the reverse command to generate the schema.xml
propel-gen . reverse
Propel 1.6.7
The only work around i have found is to remove the space in schema.xml, and then manually change each of the Base*Peer files.
e.g. Change
const POSTCODE = 'Customer.POSTCODE';
const POSTCODE = 'Customer.CURRENT';
to
const POSTCODE = 'Customer.[POST CODE]';
const POSTCODE = 'Customer.[CURRENT]';
Update:
I relised that i just need to add the 2 lines to the CustomerPeer.php not BaseCustomerPeer.php, that way i dont need to remeber it next time propel is built.

Rails: Efficiently searching by both firstname and surname

I'm trying to create a 'search box' that matches users by name.
The difficulty is that a user has both a firstname and a surname. Each of those can have spaces in them (eg "Jon / Bon Jovi", or "Neil Patrick / Harris"), and I'm wondering about the most efficient way to ensure the search is carried out on a concatenation of both the firstname and surname fields.
The list of users is quite large, so performance is a concern. I could just throw a "fullname" def in the user model, but I suspect this isn't the wisest move performance wise. My knowledge of multi-column rails indexes is weak, but I suspect there's a way of doing it via an index with a " " in it?
Just to clarify, I don't need fuzzy matching - exact match only is fine...I just need it to be run on a concatenation of two fields.
Cheers...
You could create a new field in your database called full_name with a regular index, then use a callback to populate this whenever the record is saved/updated:
before_save :populate_full_name
protected
def populate_full_name
self.full_name = "#{first_name} #{last_name}"
end
If you can modify the database, you can and should use the solution provided by gjb.
Here is the solution that does not require you to alter the database. Simply gather all the possible first-name/last-name pairs you can get from the search box. Some code:
# this method returns an array of first/last name pairs given the string
# it returns nil when the string does not look like a proper name
# (i.e. "Foo Bar" or "Foo Bar Baz", but not "Foo" or "Foo "
def name_pairs(string)
return nil unless string =~ /^\w+(\s+\w+)+$/
words = string.split(/\s+/) # split on spaces
result = []
# in the line below: note that there is ... and .. in the ranges
1.upto(words.size-1) {|n| result << [words[0...n], words[n..-1]]}
result.collect {|f| f.collect {|nm| nm.join(" ")}}
end
This method gives you an array of two-element arrays, which you can use to create an or query. Here is how the method looks:
#> name_pairs("Jon Bon Jovi")
=> [["Jon", "Bon Jovi"], ["Jon", "Bon Jovi"]]
#> name_pairs("John Bongiovi")
=> [["John", "Bongiovi"]]
#> name_pairs("jonbonjovi")
=> nil
Of course, this method is not perfect (it does not capitalise the names, but you can do it after splitting) and is probably not optimal in terms of speed, but it works. You can also reopen String and add the method there, so you can go with "Jon Bon Jovi".name_pairs.

regular expression to pull words beginning with #

Trying to parse an SQL string and pull out the parameters.
Ex: "select * from table where [Year] between #Yr1 and #Yr2"
I want to pull out "#Yr1" and "#Yr2"
I have tried many patterns, but none has worked, such as:
matches = Regex.Matches(sSQL, "\b#\w*\b")
and
matches = Regex.Matches(sSQL, "\b\#\w*\b")
Any help?
You're trying to put a word boundary after the #, rather than before. Maybe this:
\w(#[A-Z0-9a-z]+)
or
\w(#[^\s]+)
I would have gone with
/^|\s(#\w+)\s|$/
or if you didn't want to include the #
/^|\s#(\w+)\s|$/
though I also like joel's above, so maybe one of these
/^|\s(#[^\s]+)\s|$/
/^|\s#([^\s]+)\s|$/