Disadantages of defining inspect on BigDecimal - ruby-on-rails

I am using the decimal type in Rails for storing currency.
However, I am tired of constantly seeing results like:
nominal_amount: #<BigDecimal:7f919884b648,'0.7E6',9(18)>
When I use the Rails console.
I can fix this by defining inspect as def inspect; to_s; endon BigDecimal, but I am concerned that it could lead to peculiar bugs.
Anyone who can vouch for or warn against this monkey patch?

Ruby Doc suggests to override inspect for user defined classes. It returns a human readable string. No other code should rely on the functionality of inspect. So if you decide, that it is more human-readable - to you - if you change the default behavior it is okay. As long as you don't include it in a gem or other code foreign developers will use.
http://ruby-doc.org/core-2.1.1/Object.html#method-i-inspect

Hmm, if you put it in an initializer and run it in dev mode only... I don't see any risk. There is a chance someone else's code counts on BigDecimal.inspect returning an object string. I would say it's a development only modification.

inspect "Returns debugging information about the value as a string".
See apidock.com/ruby/BigDecimal/inspect
I doubt that overriding debugging output format can break anything. I'm monkey-patching it to to_s as well.

Related

How do I fix: ArgumentError: invalid byte sequence in UTF-8?

I am getting this type of error in the logs :
Parameters: {"id"=>"4", "step"=>{"documents_attributes"=>{"0"=>
{"file"=>"\x89PNG\r\n\u001A\n\u0000\u0000\u0000\rIHDR\u0000\..."}}}}
def update
#step = Step.find_by(id: params[:id])
if #step.update(steps_params)
render :json => #step
else
render :json => { :responseStatus => 402,
:responseMessage => #step.errors.full_messages.first}
end
end
During update, it rollbacks without giving any error (not execute else condition)
ArgumentError (invalid byte sequence in UTF-8):
(0.2ms) ROLLBACK
How can I fix or handle this type of request?
Your question is how to handle this type of request or error. So here is my suggestion of a general strategy.
First, do your homework. You could easily find this past question, for example. If you have tried the way already but found it did not work, you should have described what you did and what did not work in your question.
Now, I am assuming you can reproduce the case or at least you can expect you will encounter the same problem in near future (or you can wait till then) so you will have a more chance to pin down the problem next time. If you know what parameters caused the error, I guess you can reproduce the case in your development environment. However, if not, it is more tricky to pin down — it heavily depends how much information about the error and input you have and what development environment you can use, and my answer does not cover the case.
The first objective should be to pin down which command (method) exactly in your code caused an error. Did it happen just inside Rails or did your DB raise an error?
In your specific case, did it occur at Step.find_by or #step.update or else? What is steps_params? It seems like a method you have defined. Are you sure steps_params is working as expected? (You may be sure, but we don't know…)
A convenient way to find it out is simply to insert logger.debug (or logger.error) etc before and after each sentence. In doing it, it is recommended to split a sentence into smaller units in some cases. For example, steps_params and update() should be separated, such as (in the simplest case),
logger.debug 'Before steps_params'
res_steps_params = steps_params
logger.debug 'Before update'
res_update = #step.update(res_steps_params)
logger.debug 'Before if'
if res_update
# ……
Obviously you can (and perhaps should) log more detailed information, such as, res_steps_params.inspect, and you may also enclose a part with a begin-rescue clause so that you can get the detailed infromation about the exception and log it. Also, I can recommend to split update into 2 parts – substitutions and save – to find out exactly what action and parameter cause a problem.
Once you have worked out which of DB or Rails or something before (like HTTP-server or Client-browser) is to blame and which parameter causes a problem, then you can proceed to the next stage. The error message suggests it is a character-encoding issue. Is the character encoding of a string invalid (as a UTF-8), or wrongly recognised by Rails (which might be not a fault of Rails but of the client), or not recognised correctly by the DB?
Wherever the problem lies, it is usually (though not always!) possible to fix or circumvent character-encoding problems with Ruby (Rails). The Ruby methods of String#encode, String#encoding, and String#force_encoding would be useful to diagnose and perhaps fix the problem.
As an added note, it can be useful, if possible in your environment, to browse the logfile of your DB (PostgreSQL?) to find out which query passed from Rails to the DB caused a problem (if a query was indeed passed to them!). Alternatively, Rails Gem SQL Query Tracker might be handy to know what queries your Rails app create (though I have never used it and so can't tell much.)
At the end of the day, when a code misbehaves mysteriously, I am afraid only the sure way to solve is to narrow down the problematic clause or parameter step by step. Good luck!

How to analyze quickly the code definition from a given code

Let's say I'm in a really huge project and am curious how this line works:
authorize! :read_pipeline_schedule, user_project
authorize! is supposed to be method name. Is it a private function in the class, or DSL provided from a parent block, or including, or declared in a gem? Or maybe none of them?
I was using grep to find internal code and Google for external code such as gems, however I guess it's useful if I can see the call stack of the method. puts caller is printing from the place where I am although it can not be used for analyzing the above case.
I'd like to know the best practice.
You can grep your code and installed gems and, if name is unique enough, you'll quickly locate its definition and associated usages. However, if a name is a common one, like call, then this approach is useless.
Ruby is a dynamic language and, as such, is a nightmare for static analysis. You may guess where this comes from, but you just can't know for sure.
The only way to know is runtime introspection. So, in your example, put a breakpoint right before that line
binding.pry
authorize! :read_pipeline_schedule, user_project
You'll drop out to pry shell. Now you can do
show-source authorize!
And see where exactly this method is defined.
The easiest way is to ask the method itself:
method_object = method(:authorize!)
method_object.owner
# => returns module in which the method resides
method_object.source_location
# => returns file name and line number of source code where the method is defined
Note that source_location may return nil for methods which have no Ruby source code, e.g. dynamically generated methods or methods implemented in the interpreter internals (C functions in YARV, Java methods in JRuby, etc.)
I like caller(0), caller_locations, and the ever-incredible show-source

NilClass definitions

I am tired of trapping for nil when looking for a dependent record when most of the time a return of 0 will do nicely. What sort of trouble am I creating for myself by adding "id" to the NilClass thus
class NilClass
def id
0
end
end
What might the unintended consequences be? I know about exists?(), but somehow thought this might be cleaner.
Your thoughts?
If you really have a problem with this, you should use referential integrity inside your database.
If you must call methods on nil which may or may not exist or throw some kind of error, you should use either a check a la
> nil.id if nil
=> nil
or Object#try (which is part of ActiveSupport nowadays I believe?), be warned - I reckon it's kind of a code smell.
> nil.try(:id)
=> nil
That being said, it is less of a smell than modifying NilClass to do something unexpected, think of what a new developer who had to work on your project would expect.
Won't this mean you will need to check for "id == 0" to confirm existence?
Not to mention unintended consequences of overiding base Ruby functionality - it becomes really hard to predict behaviour of other libraries and core Rails APIs when you mess with language internals. Not saying it won't just work, but it's hard to be sure.
I would leave the default - it works quite nicely as Ruby allows "if object.association" expressions.

Rails.cache.fetch, Symbols, & Memcached

I have a rails 2.3.4 app and a line that looks like:
temp = Rails.cache.fetch(:temp_id) { User.find_by_name('Temp').id }
and everything worked fine, until I decided to switch the caching layer to memcached by adding the following to my environment.rb:
config.cache_store = :mem_cache_store
Now the line which used to work fine gives me the following error:
undefined method 'length' for :temp_id:Symbol
/usr/local/lib/ruby/gems/1.8/gems/activesupport-2.3.4/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb:645:in 'get_server_for_key'
I understand the error, but I would imagine this common case would have been quickly discovered by a rails test case, so I am wondering if I am doing something wrong. Otherwise, I'm sure I can monkeypatch this issue to convert the symbol to a string.
Thanks
Just use string keys if you can. All the documentation examples use string keys. Although it's not explicitly mentioned as far as I can see, other keys are not supported.
The key arguments are passed directly to the cache implementation, so the different caching flavours may disagree on whether or not they accept anything other than strings.
Because the caches are external with the exception of in-memory cache, I'm not sure that supporting symbols would be useful apart from preventing cases like yours. The key will actually be written to some output somewhere (it's not just internal to your Ruby app), so conceptually the key should be a string.
Edit in reaction to comment: yes, it is of course possible and perfectly reasonable in this case to create a monkey patch to circumvent having to change all calls. What you're suggesting is this (copied into the answer for readability):
class MemCache
def get_server_for_key_with_symbols(key, options = {})
key = key.to_s if key.is_a? Symbol
get_server_for_key_without_symbols(key, options)
end
alias_method_chain :get_server_for_key, :symbols
end
I would also consider just doing a project wide search-and-replace for \.fetch(:\w+) and replace it with \.fetch("$1") (repeat for read and write if necessary). This should probably cover 95% of all cases and a subsequent run of your test suite should catch the rest of the errors.
In general: While the documentation of Rails is pretty good these days, a lot of assumptions are unfortunately still implicit. It's generally a good idea to take a good look at the examples that are given in the documentation, and use the same style. The documented examples are always how the framework was intended to be used.
FWIW, it's canonically Rails.cache.read and Rails.cache.write.

defined? method in Ruby and Rails

I have a quite old templating system written on top of ERB. It relies on ERB templates stored in database. Those are read and rendered. When I want to pass data from one template to another I use the :locals parameter to Rails render method. For setting default variables of those variables in some templates I use the defined? method which simply tells me if local variable has been defined and if not I initialize it with default value like this:
unless defined?(perex)
perex = true
end
I am upgrading the app to latest Rails and I see some weird behavior. Basically this sometimes works (sometimes perex is undefined) and sometimes it does not (perex is defined and set to nil). This happens without anything else changing.
I have two questions:
Is there any better way other than using defined? which is proving unreliable (was reliable for several years on top Rails 1.6)? Such a way should not result in me rewriting all the templates.
I have been going through Ruby docs and was not able to find anything about defined? method. Was it deprecated or am I just plain blind?
Edit: The actual issue was caused by what seems to be a Ruby/eRB bug. Sometimes the unless statement would work, but sometimes not. The weird thing is that even if the second line got executed perex stil stayed nil to the rest of the world. Removing defined? resolved that.
First: actually, defined? is an operator.
Second: if I understand your question correctly, the way to do it is with this Ruby idiom:
perex ||= true
That'll assign true to perex if it's undefined or nil. It's not exactly what your example does, since yours doesn't evaluate the assignment when the value is nil, but if you are relying on that then, in my opinion, without seeing it, you're not writing clear code.
Edit: As Honza noted, the statement above will replace the value of perex when it's false. Then I propose the following to rewrite the minimum number of lines:
perex ||= perex.nil? # Assign true only when perex is undefined or nil
The safest way of testing if a local is defined in a Rails template is:
local_assigns[:perex]
This is documented in the Rails API together with the explanation that defined? cannot be used because of a implementation restriction.
Per mislav's answer, I went looking for that documentation in the Rails API, and found it in Class ActionView::Base (under the heading "Passing local variables to sub templates"). It was hardly worth the search, though, since it barely said anything more than mislav did. Except that it recommends this pattern:
if local_assigns.has_key? :perex
Taking into considerationg mislav's original answer and KenB's elaboration, I think the following is the absolute best approach (though I'm open to opinion). It utilizes Ruby's Hash#fetch method to fallback on an alternate value if the key does not exist in the original hash.
perex = local_assigns.fetch(:perex, true)
This is even better than the ||= method that most users will suggest since sometimes you will want to allow false values. For example, the following code will never allow a false value to be passed in:
perex = local_assigns[:perex] || true

Resources