I'd like to propose the Object#nonblank? in ActiveSupport, layered
over the blank? method. This has been working well for us in the past
year and hopefully others will find it useful enough to include in
core.
It is analogous to Ruby's Numeric#nonzero? method: it either returns
the object itself (if not blank) or nil (if blank). This makes it easy
to treat blank parameters the same as missing ones, and allows
chaining:
For example, this:
state = params[:state] unless params[:state].blank?
country = params[:country] unless params[:country].blank?
region = state || country || 'US'
becomes:
region = params[:state].nonblank? || params[:country].nonblank? ||
'US'
The ticket is here:
along with a patch that includes full documentation and tests.
> --
>
> You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
> To post to this group, send email to rubyonra...@googlegroups.com.
> To unsubscribe from this group, send email to rubyonrails-co...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en.
>
>
>
--
Cheers!
- Pratik
http://m.onkey.org | http://twitter.com/lifo
True, but present? is just the inverse of blank?. What we've found
very useful is to have a method that treats blank parameters the same
as missing ones, and allows chaining.
Check out the example:
state = params[:state] unless params[:state].blank?
country = params[:country] unless params[:country].blank?
region = state || country || 'US'
Rewriting with present? isn't any more expressive:
state = params[:state] if params[:state].present?
country = params[:country] if params[:country].present?
region = state || country || 'US'
But nonblank? makes it more concise and expressive IMO:
region = params[:state].nonblank? || params[:country].nonblank? ||
'US'
We've been using it this way for the last year and it really has
cleaned up a lot of code. More importantly it has become a reflex to
use it inline--as shown above--that has helped us avoid bugs where we
might think a parameter was present but really it was just there with
an empty value.
I suppose we could keep the method name present? but switch its
behavior to match what's proposed here for nonblank?. But that
contract change could break someone's code who was depending on a
boolean being returned. Also I prefer that nonblank? has a name that
parallels nonzero? from Ruby.
I like the symmetrical pair of nonzero? and nonblank? because they map
values (0, empty/blank string respectively) to nil that are typically
equivalent to not being present at all. Other languages like Python
found it convenient to have 0 and empty string treated as false for
just this reason I think.
-Colin
On Dec 27, 5:37 am, Pratik <pratikn...@gmail.com> wrote:
> We already have Object#present? :)
>
>
>
>
>
> On Sun, Dec 27, 2009 at 7:49 AM, ColinDKelley <colindkel...@gmail.com> wrote:
> > All,
>
> > I'd like to propose the Object#nonblank? in ActiveSupport, layered
> > over the blank? method. This has been working well for us in the past
> > year and hopefully others will find it useful enough to include in
> > core.
>
> > It is analogous to Ruby's Numeric#nonzero? method: it either returns
> > the object itself (if not blank) or nil (if blank). This makes it easy
> > to treat blank parameters the same as missing ones, and allows
> > chaining:
>
> > For example, this:
>
> > state = params[:state] unless params[:state].blank?
> > country = params[:country] unless params[:country].blank?
> > region = state || country || 'US'
>
> > becomes:
>
> > region = params[:state].nonblank? || params[:country].nonblank? ||
> > 'US'
>
> > The ticket is here:
>
> >https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/3...
region = [params[:state], params[:country], 'US'].detect(&:present?)
I am -1 because I think a predicate should document only a boolean
contract.
On the other hand it is something you can easily add to your app, as
you've been doing.
Sent from my iPhone
El 27/12/2009, a las 17:58, ColinDKelley <colind...@gmail.com>
escribió:
> To post to this group, send email to rubyonrails-
> co...@googlegroups.com.
As for predicate methods returning true or false, the Numeric#nonzero?
to which the OP compares nonblank? is a perfect counter example. The
way that it was explained to me (by Jim Weirich, iirc) was that a
predicate should return either false/true or nil/"not nil" (where "not
nil" is typically a useful object). This is precisely how
Numeric#nonzero? behaves.
I like nonblank? for the same reason that I dislike present? -- I
*know* that " ".blank? is true, but " ".present? doesn't strike me as
necessarily false like " ".nonblank? does.
-Rob
On Dec 27, 10:43 am, Duncan Beevers <duncanbeev...@gmail.com> wrote:
> Including all the items in an Array and picking the first present one
> circumvents any opportunity to use logic short-circuiting to avoid
> unnecessary, potentially expensive calculation, not to mention unnecessary
> allocation of container objects and the overhead of symbol-to-proc.
Yes, that's too much overhead, Here's the benchmark:
>> state=nil; country='Canada';
>> Benchmark.bm(10) do |bm|
>> bm.report("nonblank?") { n.times { region = state.nonblank? || country.nonblank? || 'US' } }
>> bm.report("detect ") { n.times { region = [state.nonblank?, country, 'US'].select &:present? } }
>> end
user system total real
nonblank? 0.210000 0.000000 0.210000 ( 0.212206)
detect 2.320000 0.070000 2.390000 ( 2.487010)
Over 10X slower! The above example doesn't emphasize short-circuiting
either. You could easily find examples that were 100X slower because
of complex expressions.
I definitely wouldn't write it that way. If I had to live without
nonblank?, this is what I'd write:
region = (params[:state] unless params[:state].blank?) ||
(params[:country] unless params[:country].blank?) ||
'US'
but you can see that's not DRY since every argument is mentioned
twice. That's what led us to add nonblank?. It's concise, DRY, and
performs well.
On Dec 27, 11:51 am, Yehuda Katz <wyc...@gmail.com> wrote:
> My biggest concern with nonblank? is that idiomatic Ruby is to return a
> boolean from question mark methods.
Generally that's true, but Ruby has nonzero? which behaves exactly
like nonblank?: you get a value back that is _equivalent_ to boolean
but has other uses too. Another Ruby example is =~ which returns nil
or an index, although that doesn't end in ?.
> I like Pratik's solution, and would like to see a scenario where the short-circuit logic is important
I just grep'd our company's code base (65K LOC) and found 190 usages
of nonblank?, not including the definition and tests. About 70% are
using it as a boolean (we haven't used present? since that was
introduced in Rails 2.2 and we were on 2.1 until the end of the
summer) but 30% are the pattern discussed here. For example, from our
EmailAddress class:
@friendly_name = tmail.name.nonblank? || friendly_name
I also found several cases where the mapping to nil was explicitly
important (because we wanted it to the be the same as "not present")
as in
vid = cookies[:vid].nonblank?
Of the 70% that treat nonblank?'s result as a boolean, I didn't find a
single case that put !! in front to force the result to a proper
boolean. Likely that's because when you really want a true boolean, !
blank? reads best.
-Colin
Changing the subject a bit, I've just remembered a feature of Groovy's
language that I like and think that Ruby lacks...
In Groovy:
result = object?.method?.another
would mean in Ruby something like:
result = object && object.method && object.method.another
I wonder if it would be possible to have a similar constructor in Ruby
that was more DRY like in Groovy. Groovy's syntax would not be an option
to Ruby since Ruby's methods accept method names ending with '?', which
is not possible in Groovy. That is a good think but I can't think in an
expressive annotation to be added to Ruby...
Maybe "object#.method?#.to_s". It definitely has nothing to do with
Rails, but maybe there is some way of getting a similar result that is
already implemented in ActiveSupport and that I don't know, so I hope
you don't mind I ask it here...
If you do so, I'm sorry and I promise I won't do it again.
Regards,
Rodrigo.
We just tried running through 20 candidates in the chat room and
didn't come up with anything worthwhile. We need something that's a
single word, like "present?" or "blank?" but doesn't use a predicate
and is exceptionally clear about what's going on.
--
DHH
Like:
object.when_present || another_object
It is not a single word, but it is expressive.
'or' would be great but there is a problem:
object.or another_object
The problem is that another_object would be evaluated always...
object.or || another_object # also does not look great...
What do you think?
Best regards,
Rodrigo.
I gave it some more thought and came up with a great name:
Object#presence. It plays off the existing present? and offers
something nice and clear.
Commit is at http://github.com/rails/rails/commit/1c47d04ea5ac19601b316daf8fdc6f38c50eec73
Great suggestion, Colin. Love it with a great name.
--
DHH
Rodrigo,
Funny you should mention that! Hobo Support has the _? operator that
does just what you want. (They mention in the documentation that they
wanted it to be called ?, but had to go with ._? to be valid Ruby.)
We use _? extensively in our application and frankly couldn't live
without it. Here's a recent thread where I contributed a simplified
implementation of that method (inspired by ActiveRecord's
AssociationProxy BTW):
https://hobo.lighthouseapp.com/projects/8324/tickets/537-safenil-with-nil-public-methods-as-in-_to_i
-Colin
On Dec 27, 5:56 pm, DHH <david.heineme...@gmail.com> wrote:
> > We just tried running through 20 candidates in the chat room and
> > didn't come up with anything worthwhile. We need something that's a
> > single word, like "present?" or "blank?" but doesn't use a predicate
> > and is exceptionally clear about what's going on.
>
> I gave it some more thought and came up with a great name:
> Object#presence. It plays off the existing present? and offers
> something nice and clear.
>
> Commit is athttp://github.com/rails/rails/commit/1c47d04ea5ac19601b316daf8fdc6f38...
> As for predicate methods returning true or false, the Numeric#nonzero?
> to which the OP compares nonblank? is a perfect counter example. The
> way that it was explained to me (by Jim Weirich, iirc) was that a
> predicate should return either false/true or nil/"not nil" (where "not
> nil" is typically a useful object). This is precisely how
> Numeric#nonzero? behaves.
No, no. Predicates return true or false _values_. The implementor
decides which one he wants to return, and it is not required/expected
that it is is one of the singletons true/false. In that sense nonzero?
is not a counter-example.
But you are not going to do arithmetic with the value returned by
nonzero? right?
Predicates should only have a boolean contract, the chosen returned
values shouldn't be documrented or relevant to client code.
Well IMHO, I think it's more idiomatic Ruby to have a predicate only
worry about the truthiness or falsiness of what it returns. Any value
other than nil or false is a perfectly good, and true return value
from a predicate, and in many cases more useful.
Insisting that a predicate return either true or false, smells more
Java or C++ish than Rubyish to me.
But what do I know?
--
Rick DeNatale
Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
>
> Funny you should mention that! Hobo Support has the _? operator that
> does just what you want. (They mention in the documentation that they
> wanted it to be called ?, but had to go with ._? to be valid Ruby.)
> We use _? extensively in our application and frankly couldn't live
> without it. Here's a recent thread where I contributed a simplified
> implementation of that method (inspired by ActiveRecord's
> AssociationProxy BTW):
>
> https://hobo.lighthouseapp.com/projects/8324/tickets/537-safenil-with-nil-public-methods-as-in-_to_i
Wandering ever-more-offtopic, but does anybody know exactly why Ruby
1.9 whines when removing object_id? It warns that doing so "may cause
serious problem", but I've never seen a discussion of exactly *what*
problem. I noted (on the above ticket) that AssociationProxy and Scope
carefully avoid undefining object_id, but is that solely because of
the warning?
--Matt Jones
user.nickname || user.name
Now we have #presence which lets us do:
user.nickname.presence || user.name
That's reasonably concise, but just keep in mind that all of this is
just to make Ruby act more like Perl where empty strings are false-ish
values. I'm sure there are other use cases for #presence, but empty
strings that come from form submissions seem like at least 90% of the
issue.
--josh
> --
>
> You received this message because you are subscribed to the Google
> Groups "Ruby on Rails: Core" group.
> To post to this group, send email to rubyonrails-
> co...@googlegroups.com.
> To unsubscribe from this group, send email to rubyonrails-co...@googlegroups.com
> .
> For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en
> .
>
>
--
Josh Susser
http://blog.hasmanythrough.com
I agree that this usage comes up often in controllers, but it is
common in models too. It seems to me that this issue comes up any
time you want to map the "empty"/"blank" value to be the same as the
"not present" value, whether those came from a form, the database, or
read from a file... Yes, if Ruby had chosen empty strings to be false-
ish (like Perl & Python) that would have helped.
Numeric is exactly parallel, where it's often appropriate to map 0 to
be the same as the "not present" value, nil. nonzero? does that,
which can make up for the fact that Ruby considers 0 to be true-ish.
(BTW whenever possible we choose our string attributes to be null =>
false in the DB so we don't have to write code that deals with two
types of empty. Of course there are cases when you really want a
blank/empty string to be one kind of empty and nil/NULL to be another,
but that is << 1% case IME.)
Do Rails forms have any convention for posting the "nil" value?
They've now got the special _delete key for nested forms. It seems
like it would be valuable to have a similar special representation to
indicate that a column should be set to nil. (We were doing some
composed_of attributes last month and really would have found that
handy. Instead we have ugliness in our controller to impose a
convention like that on the params values before updating the model.)
-Colin
> That's reasonably concise, but just keep in mind that all of this is
> just to make Ruby act more like Perl where empty strings are false-ish
> values. I'm sure there are other use cases for #presence, but empty
> strings that come from form submissions seem like at least 90% of the
> issue.
Maybe we could eliminate that use case by nullifying blank strings where appropriate:
http://github.com/pixeltrix/nullify_blanks
The empty strings in my databases always bugged me so I wrote the above plugin to fix that. It only nullifies a blank string where a column exists, is a text column and accepts nulls.
Andrew
--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
To post to this group, send email to rubyonra...@googlegroups.com.
http://dev.rubyonrails.org/ticket/5694
BTW, here's my take on a solution using a before filter to recursively
coerce empty strings to nil:
Scratch the HTML forms itch, and perhaps the need for another method
on Object falls below the threshold of irritation.
One last thought: does Ruby 1.9 provide any guidance here?
> I think the problem is that there is no way to distinguish between a nil value and a blank value coming from the request. They both look the same and they both have their uses. I don't think your solution solves this problem, because there are likely cases like above where blank has significance.
In the case where a column is set to :null => false then the plugin won't nullify the value. I don't know about your end users but mine wouldn't understand the difference between a nil value and an empty string - relying on them to determine what a value should be is not something I want to push on them. Also blank number fields are already converted to nil.
Andrew
Cheers,
Mike D.
Yes, we've all been wondering what the risk is there. Worst case I
guess we could grep through the Ruby source I suppose.
Tom Locke on the Hobo project wondered
> OTOH maybe it's like crossing the [streams] in Ghostbusters: you just don't do it and you don't ask why : )
I've never been a fan of superstition though. Doesn't that just lead
to a "cargo cult?" :-)
I wondered if maybe Object#equal? used object_id to determine if the
two objects were the same. But that theory was disproven by a test
that undef'd it first: equal? ran just fine. I even tried def'ing
equal? to raise an exception and that showed it never got invoked by
equal? anyway.
-Colin
--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
To post to this group, send email to rubyonra...@googlegroups.com.