I am trying to convert the string into classname using constantize. But it throws error if the classname is not present or the string is empty or nil.
Is there any way to convert the string into classname without throwing error. Or returning false only if not able to convert.
I hate Rails, it brings a ton of redundant so-called helpers, having zero value in general. This might be easily done with pure Ruby:
Kernel.const_get(string) if Kernel.const_defined?(string)
The above effectively returns the class or nil (which is falsey in Ruby) if the class is not defined.
Firstly, I want to point out something from the answers above. rescue false means that all kinds of exceptions thrown from that expression are rescued as false. This means that even if you have an exception object of class NoMethodError or RuntimeError you will return false and your expectation would be that the constantized string does not match a constant in your app. This can cause you hours of debugging if you don't know how the error handling system in ruby is working. It is also a place to introduce a lot of bugs in your code in the future.
I see the ruby-on-rails tag so I assume you are running a problem in a rails app. You can use a helper method coming from the ActiveSupport::Inflector module. Instead of rescuing the constantize method you would probably want to use the safe_constantize. It will return nil in case the constant is not present in your project.
Example usage (note I haven't defined a Foo constant in my project):
# with constantize
irb(main) > 'foo'.constantize
=> NameError (wrong constant name foo)
# with safe_contantize
irb(main) > 'foo'.safe_constantize
=> nil
What's the error? if it's an exception you can do something like:
the_class = your_string.constantize rescue false
The rescue catches de exception and returns false in that case.
You can achieve it by following,
string.constantize rescue false
Additional
If you have have downcase string or table name for that, it can also be converted into class name by using classify method as follows,
string.classify.constantize rescue false
If you have string 'event_managers', it will return class EventManager
You can provide a default value or return early in case of nil and then rescue a NameError:
def to_class(name)
# default to empty string
(name || "").constantize
rescue NameError
false
end
def to_class(name)
# guard against invalid input
return false if name.nil?
name.constantize
rescue NameError
false
end
EDIT: It's longer than simple
string.constantize rescue false
but IMO you should rescue the most specific error you can. rescue false is OK for simple scripts or some test cases. In production code it's quite risky - you could hide some exceptions that should be verbose.
Related
I have this code
args = x.lead_details.last.leads.last.country_id rescue nil
Function(args)
I have used rescue keyword to make sure I don't get errors like
undefined method `country_id' for nil:NilClass,
undefined method `leads' for nil:NilClass
Is there any better way I can achieve this?
Other approach is
Function(x.lead_details.last.leads.last.country_id if x.present? && x.lead_details.present? && x.lead_details.last? && so on)
rescue nil is evil. It does not just rescue nil errors. It eats everything you throw at it. For example:
class Foo
def bar
end
def woo
raise ArgumentError
end
end
Foo.new.baz rescue nil # nil!
Foo.new.woo rescue nil # nil!
This applies to any inline rescue.
Ruby 2.3.0 introduced the safe navigation operator that lets you do:
x&.lead_details&.last&.leads&.last&.country_id
ActiveSupport also has a Object#try! method that can be used in older versions of Ruby:
x.try!(:lead_details).try!(:last).try!(:leads).try!(:last).try!(:country_id)
But you're really just pissing on the Law of Demeter here and you should refactor the code so you don't have to traverse down that insane method chain as you're really just burying tons of potential bugs and making debugging impossible.
See:
5 reasons not to use safe navigation operators
You can use the safe navigation operator (&.) that was introduced in Ruby 2.3:
Function(x&.lead_details&.last&.leads&.last&.country_id)
It returns nil when you try to call a method on nil.
I'm upgrading a Rails application I've inherited from 3.2 to 4.0.1. I followed and finished the edge guide here:
http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#upgrading-from-rails-3-2-to-rails-4-0
I've gotten everything fixed except for a single error that I can't seem to find the root cause of. When I attempt to save a User model object, I'm met with the following error:
[1] pry(main)> User.create(name: "test user", email: "testuser#frobnitz.com", password: "testPassword123", password_confirmation: "testPassword123")
(0.6ms) BEGIN
(0.9ms) ROLLBACK
NoMethodError: undefined method `to_datetime' for false:FalseClass
from /home/cmhobbs/src/serve2perform/.gem/ruby/2.3.0/gems/activesupport-4.0.1/lib/active_support/core_ext/date_time/calculations.rb:161:in `<=>'
activesupport 4.0.1 and rals 4.0.1 are installed. I use chgems and I purged my .gem/ directory and Gemfile.lock before bundling again.
Here is a Gist of the User model.
And here is all of the backtrace output I could get from pry.
Here is a link to the User table schema.
Once you've found the offending callback to be this one:
before_create :activate_license
def activate_license
self.active_license = true
self.licensed_date = Time.now
end
things begin to be clearer. The activate_licence is a before callback. Before callbacks can halt the whole callbacks chain by returning false (or raising an exception).
If we look carefully in the debug output that you provided by manually adding some puts lines into the Rails callbacks code, we can indeed find the comparison of this callback result with false (here - I removed some unimportant parts of the code):
result = activate_license
halted = (result == false)
if halted
halted_callback_hook(":activate_license")
end
Because the support for halting before callbacks by returning false (i.e. the Rails code shown above) practically has not changed from Rails 3.2 to Rails 4.0.1, the issue must lie in the comparison itself.
The callback returns a DateTime object (it's the last assignment in the method which is also returned). And, indeed, the comparison of DateTimes changed significantly between the two Rails versions (also note that the == operator is normally evaluated using the <=> operator):
in Rails 3.2 it was this:
def <=>(other)
if other.kind_of?(Infinity)
super
elsif other.respond_to? :to_datetime
super other.to_datetime
else
nil
end
end
notice especially the respond_to? check if the other object is also a date or time object while otherwise returning nil.
whereas in Rails 4.0.1 this changed to the bare code below:
def <=>(other)
super other.to_datetime
end
→ all sanity checks are gone!
Now, everything is clear: the result of the callback (a DateTime object) is compared using the <=> operator with false and under Rails 4.0, the comparison tries to convert the false object to DateTime without any sanity checks, which of course fails and throws the exception.
To fix this issue, simply ensure that your callback returns something that Rails can compare with false without any problems, e.g. true, as your callback is never supposed to halt the chain:
def activate_license
self.active_license = true
self.licensed_date = Time.now
true
end
Now everything should work as expected again.
You can bind even in core classes, please do something like this and check what the other is, from where it came from.
/home/cmhobbs/src/serve2perform/.gem/ruby/2.3.0/gems/activesupport-4.0.1/lib/active_support/core_ext/date_time/calculations.rb
def <=>(other)
binding.pry
if other.kind_of?(Infinity)
super
elsif other.respond_to? :to_datetime
super other.to_datetime rescue nil
else
nil
end
end
I'm trying to update the attributes of an Object, but often the Object i try to update doesn't exist anymore.
E.g.: I'm post-processing a CSV file to get the attributes:
array.each do |a|
player = Player.find_by_grepo_id(a[:grepo_id])
player.update_attributes(a)
end
That will throw an error when a Player is not found.
What i learned from previous experiences:
ActiveRecord::Base.find always throws an exception if it does not find a record, this is intentional.
I should only use find if I'm absolutely expect to have whatever it is I'm looking for.
If I'm rending a show action and can't find the article, I should rescue that exception and render a 404 (Not found)
instead of redirecting to index (technically).
If I want to find something by it's id attribute without forcing an exception, I should use the dynamic finder
find_by_id (In my case find_by_grepo_id) which will return false if it doesn't find a record with that id.
But upon running the task which contains the above code i get
NoMethodError: undefined method `update_attributes' for nil:NilClass
That's because a Player with that specific id doesn't exist anymore. If i wrap the update_attributes call in a .present? method it works.
What am i missing? Shouldn't the find_by_id method NOT throw an error and just skip it ?
If you want to do it in one call instead of two, you can use the update_all method like this:
Player.where(:grepo_id => a[:grepo_id]).update_all(a)
This will result in the following SQL:
UPDATE players SET ... = ..., ... = ... WHERE players.grepo_id = ...
Also works if the grepo_id doesn't exist: nothing will get updated. Note however that this just runs the SQL; any validations or callbacks on your model are ignored.
This is due to you are doing update_attributes even if it does not find the record by grepo_id. find_by_grepo_id returns nil if it does not find any record. So you need to add a condition to get rid of this error.
array.each do |a|
player = Player.find_by_grepo_id(a[:grepo_id])
player.update_attributes(a) if player.present?
end
Rails has a try method (check the docs) and you can use it here:
array.each do |a|
player = Player.find_by_grepo_id(a[:grepo_id])
player.try do |p|
p.update_attributes(a)
end
end
This should work fine and updating attribute or silently failing (without throwing exceptions) when no record is found.
I am using Ruby on Rails 3.2.2 and I would like to properly rescue the following process flow by raising a "custom" error message:
def rescue_method
# sample_string.class
# => String
# sample_string.inspect
# => "ARubyConstantThatDoesNotExist"
begin
build_constant(sample_string)
rescue
raise("My custom error message: #{build_constant(sample_string)} doesn't exist.")
end
end
def build_constant(sample_string)
"AModule::#{sample_string}".constantize
end
Note: I feel "forced" to use the constantize method also in the raised "custom" message in order to DRY code...
When the rescue_method is executed it seems that the raise("My custom error message") code is never executed and I get the following error:
uninitialized constant AModule::ARubyConstantThatDoesNotExist
How to properly display the raised "custom" message (since a further error exception is raised in the subsequent raised "custom" message)? What do you advice about?
The problem is that your build_constant method is doing two things:
Building the class name.
Turning the name into a class using constantize.
One of those things wants to use the other when an exception is raised. A simple solution is to pull those separate tasks apart:
def build_class_name(sample_string)
"AModule::#{sample_string}"
end
def rescue_method
name = build_class_name(...)
name.constantize
rescue NameError
raise("My custom error message: #{name} doesn't exist.")
end
You should also be more specific about the exception you're looking for so I added that for free.
If you don't want to rely on catching any exception, you could use safe_constantize (https://apidock.com/rails/ActiveSupport/Inflector/safe_constantize).
Same purpose as constantize but will return nil instead, whenever a Module does not exist.
'UnknownModule::Foo::Bar'.safe_constantize # => nil
begin
"AModule::#{sample_string}".constantize
rescue SyntaxError, NameError => err
raise("My custom error message")
end
I'd like to verify that all translation calls in my app pass the proper interpolation arguments. However, when no argument is passed, there is no exception raised.
Per Rails i18n documentation http://guides.rubyonrails.org/i18n.html
"If a translation expects an interpolation variable but it has not been passed to #translate an I18n::MissingInterpolationArgument exception is raised."
However, this doesnt seem to be the case if NO interpolation variables are passed. For instance, this code doesn't raise an exception:
require 'i18n'
I18n.backend.store_translations :en, :thanks => 'Thanks %{name}! '
I18n.translate :thanks # <- expect an exception, but it just returns 'Thanks %{name}!'
I18n.translate :thanks, :foo => 'bar' # <- this raises
Anyone know of a way to detect calls that are missing translation keys during the tests other than parsing for "%{" ?
This looks like a rail bug to me. Why don't you try patching it?