25
\$\begingroup\$

I installed settingslogic and in the configuration file I put the regex for the email as follows:

#config/settings.yml
defaults: &defaults
 email_regex: /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i
development:
 <<: *defaults
 # neat_setting: 800
test:
 <<: *defaults
production:
 <<: *defaults

That I load in devise configuration in this way:

#config/initializers/devise.rb
 # Regex to use to validate the email address
 # config.email_regexp = /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i
 config.email_regexp = eval Settings.email_regex

It works, but what do you think about that eval? Is it the correct way to convert a string to a regex?

Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
asked Jan 22, 2011 at 18:09
\$\endgroup\$
5
  • \$\begingroup\$ Did you choose email only as an example or are you actually making the regexp pattern that matches emails a configurable option? If the latter, allow me to ask: why? Are you really planning to deploy multiple versions of this app that need to match emails in different ways? \$\endgroup\$ Commented Jan 27, 2011 at 16:43
  • \$\begingroup\$ sorry for late answer, I store several email around the prj and I taught to have a single place where to store the regex all the emails have to respect, a constant would be enough \$\endgroup\$ Commented Jan 29, 2011 at 21:58
  • \$\begingroup\$ the solution is for getting the regex from a yaml, while if you have it in a regular string this could help you stackoverflow.com/questions/4840626/… \$\endgroup\$ Commented Feb 2, 2011 at 19:09
  • \$\begingroup\$ Sorry for my even later reply. To clarify, I was asking why this needs to be set in a config file at all instead of being somewhere in the code where it makes sense - only once of course. E.g. something like this: Email.valid?(string) \$\endgroup\$ Commented Feb 8, 2011 at 23:01
  • \$\begingroup\$ The string is read in devise configuration, it's not my own auth and I use Settingslogic to store all my config in one place. \$\endgroup\$ Commented Mar 10, 2011 at 9:11

5 Answers 5

28
\$\begingroup\$

If you want to use a regexp in a yaml file you need to use !ruby/regexp

#config/settings.yml
defaults: &defaults
 email_regex: !ruby/regexp '/^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i'

Edit: The solution proposed by Mike Bethany is very similar to the yaml implementation.

You can take a look to what is used in Ruby 1.9.2 here (search for "!ruby/regexp"): https://github.com/tenderlove/psych/blob/master/lib/psych/visitors/to_ruby.rb

PS (and OT): I think, like Mike Bethany, that this basic functionality belong to the Regexp class not yaml, and need to be moved to a Regexp method. What do you think?

answered Jan 27, 2011 at 2:35
\$\endgroup\$
0
9
\$\begingroup\$

I'm not crazy about using eval for such a simple task, it's easy and it works but it just doesn't sit well with me; it's like giving your gran' an Indy car to go get a loaf of bread. Instead you could do something like this.

split = Settings.email_regex.split("/")
options = (split[2].include?("x") ? Regexp::EXTENDED : 0) |
 (split[2].include?("i") ? Regexp::IGNORECASE : 0) |
 (split[2].include?("m") ? Regexp::MULTILINE : 0) unless split[2].nil?
Regexp.new(split[1], options)

This will work if there are options or not and doesn't require a potentially dangerous eval.

P.S. Sinetris made the much better suggestion of just adding !ruby/regexp before your regex and wrapping it in single quotes in your settings.yml file. That still doesn't fix the issue with the RegExp class not properly dealing with string representations of regex statements though so I'll leave the above code for anyone that wants to do that outside of a YML file.

answered Jan 22, 2011 at 19:58
\$\endgroup\$
7
  • \$\begingroup\$ I agree on everything and by the way thanks for editing, where then would you put this function for reuse? \$\endgroup\$ Commented Jan 22, 2011 at 23:16
  • \$\begingroup\$ It's a little verbose anyway... \$\endgroup\$ Commented Jan 22, 2011 at 23:17
  • 1
    \$\begingroup\$ It's almost criminal that this kind of basic functionality isn't in the Regexp class so I'd monkey patch it into there. I would use a new function name though like Regexp#new_from_string or whatever you think sounds best. (I just added the "ruby" tag since it's more of a Ruby question than a Rails one). \$\endgroup\$ Commented Jan 23, 2011 at 2:13
  • \$\begingroup\$ It's probably not part of the class because you can just prepend (?xim) to the regex itself. Monkeypatching a new constructor into a core class because the default doesn't do what you want /at first glance/ is a little nuts. \$\endgroup\$ Commented Jan 27, 2011 at 17:11
  • \$\begingroup\$ I think you may not have bothered to read the actual question. Your comment makes no sense otherwise. The issue has nothing to do with regex options but converting a string representation of a regex to an actual regex. I'll bet you feel pretty nuts yourself now huh? Next time you might find it benificial to actually read the whole issue before commenting. \$\endgroup\$ Commented Jan 27, 2011 at 20:45
6
\$\begingroup\$

I would definately not put the regex in the comment. That means that it needs to be changed in two places, one of which doesn't matter and will be misleading. Place a comment on the declaration of email_regex that explains what it is filtering. That way if it ever changes, all the places to change are contained and easy to find.

#config/initializers/devise.rb
 # Regex to use to validate the email address
 config.email_regexp = eval Settings.email_regex 

and

# Email validation regex - <short explanation to taste>
email_regex: /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i 

I see no readability problems with the code, if that is the correct method of retrieving a setting. (I'm not a Ruby expert.)

answered Jan 22, 2011 at 18:53
\$\endgroup\$
1
  • \$\begingroup\$ the comment was the old code before I put the setting, I left in there in case I had to go back to the old solution, you right, I'll remove it or comment it better. \$\endgroup\$ Commented Jan 22, 2011 at 19:34
4
\$\begingroup\$

If you want avoiding the eval you can. It's a little more trick but you can be sure, you have a regexps after this code in your devise :

#config/initializers/devise.rb
# Regex to use to validate the email addres
# config.email_regexp = /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i
Setting.email_regexp[/\/(.*)\/(.?)/] 
config.email_regexp = x ; Regexp.new(/#{1ドル}/)

The problem with this trick is you failed the second argument in your case the insensitive-case. You just need add a new setting like :

email_regexp_sensitive_case: true

And now you just need call like this :

#config/initializers/devise.rb
# Regex to use to validate the email addres
# config.email_regexp = /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i
Setting.email_regexp[/\/(.*)\/(.?)/] 
config.email_regexp = Regexp.new(/#{1ドル}/, Setting.email_regexp_sensitive_case)

In my case you are sure to have a Regexp define in your email_regexp without any eval.

answered Jan 23, 2011 at 0:23
\$\endgroup\$
1
  • \$\begingroup\$ but what do you think about the code the way it was? is there anything we should be aware of (using eval this way)? because right now the initial implementation is the one that offers more readability \$\endgroup\$ Commented Jan 23, 2011 at 14:46
4
\$\begingroup\$

As Sinetris mentioned, YAML has support for loading an instance of Regexp from a string.

require 'yaml'
YAML.load('!ruby/regexp /abc/ix')
# => /abc/ix
YAML.load('!ruby/regexp /abc/ix').class
# => Regexp 

http://apidock.com/ruby/Regexp/yaml_new/class

answered Jan 27, 2011 at 13:26
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.