23
\$\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?

\$\endgroup\$
  • \$\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\$ – user804 Jan 27 '11 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\$ – ecoologic Jan 29 '11 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\$ – ecoologic Feb 2 '11 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\$ – user804 Feb 8 '11 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\$ – ecoologic Mar 10 '11 at 9:11
27
\$\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?

\$\endgroup\$
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.

\$\endgroup\$
  • \$\begingroup\$ I agree on everything and by the way thanks for editing, where then would you put this function for reuse? \$\endgroup\$ – ecoologic Jan 22 '11 at 23:16
  • \$\begingroup\$ It's a little verbose anyway... \$\endgroup\$ – ecoologic Jan 22 '11 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\$ – Mike Bethany Jan 23 '11 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\$ – Eevee Jan 27 '11 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\$ – Mike Bethany Jan 27 '11 at 20:45
7
\$\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.)

\$\endgroup\$
  • \$\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\$ – ecoologic Jan 22 '11 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.

\$\endgroup\$
  • \$\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\$ – ecoologic Jan 23 '11 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

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.