3 times ActiveSupport 3
- February 3rd, 2010
- Posted in Ruby . Ruby on Rails . Tips
- Write comment
Rails 3 is coming. All the big changes are spoken of elsewhere, so I’m going to mention some small changes. Here are 3 random new methods added to ActiveSupport:
presence
First up is Object#presence which is a shortcut for Object#present? && Object. It is a bit of a sanitizer. Empty strings and other blank values will return nil and any other value will return itself. Use this one and your code might be a tad cleaner.
"".presence # => nil "foo".presence #=> "foo" # without presence: if params[:foo].present? && (foo = params[:foo]) # .. end # with presence: if foo = params[:foo].presence # ... end # The example Rails gives: state = params[:state] if params[:state].present? country = params[:country] if params[:country].present? region = state || country || 'US' # ...becomes: region = params[:state].presence || params[:country].presence || 'US'
I like this way of cleaning up you’re code. I guess it’s Rubyesque to feel the need to tidy and shorten your code like this.
uniq_by
Another funny one is Array.uniq_by (and it sister-with-a-bang-method). It works as select, but returns only the first element from the array that complies with the block you gave it. Here are some examples to illustrate that:
[ 1, 2, 3, 4 ].uniq_by(&:odd?) # => [ 1, 2 ]
posts = %W"foo bar foo".map.with_index do |title, i|
Post.create(:title => title, :index => i)
end
posts.uniq_by(&:title)
# => [ Post("foo", 0), Post("bar", 1) ] ( and not Post("foo", 2) )
some_array.uniq_by(&:object_id) # same as some_array.uniq
exclude?
And the final one for today is exclude? which is the opposite of include?. Nobody likes the exclamation mark before predicate methods.
# yuck: !some_array.include?(some_value) # better: some_array.exclude?(some_value)
And it also works on strings:
# even more yuck:
!"The quick fox".include?("quick") # => false
# better:
"The quick fox".exclude?("quick") # => false
The full release notes of Rails 3 can be read here.
[ 1, 2, 3, 4 ].uniq_by(&:odd?) # => [ 1, 2 ]
Is it right?
I checked the source code, it is written
def uniq_by
hash, array = {}, []
each { |i| hash[yield(i)] ||= (array << i) }
array
end
I think it should be:
def uniq_by
hash, array = {}, []
each { |i| !yield(i) || hash[i] ||= (array << i) }
array
end
or Maybe this version is more readable:
def uniq_by
hash, array = {}, []
each { |i| hash[i] ||= (array << i) if yield(i)}
array
end
@zigzag you’re wrong, because uniq_by isn’t just about it’s boolean value, but also other values, like string. For example:
posts.uniq_by { |post| post.name.downcase }This is why the results of the block are used as keys in the temporary hash.
I think the description in my post wasn’t really accurate. It’s not a select in that it’s not important if the block returns true or false. It will even treat false and nil differently:
[ true, false, nil, 1, 2, nil, 2, false ].uniq_by { |x| x } #=> [ true, false, nil, 1, 2 ]@Iain Hecker
Yes, you are right.
I read it again, from the your “post name” example i figured out what the usage it is.
Thanks.
Looks good – I know that rails 3.0beta1 is trunk, but any idea on when it will be released as a final release? Is it a good idea to start writing apps in it, or should I wait for the final release?
I really like the changes, and it’ll be nicer to use more ruby to condense the code instead of relying on something else, along with shortcuts like present?, and various helper methods to replace ‘unless obj.nil? || obj.blank?’.
You never have to rely on both obj.nil? and obj.blank? as blank? will check for nil? too…
Anyway, there are a lot of plugins and gems still not working on Rails 3. I wouldn’t upgrade your app just yet. If you have plugins, please make sure they work and put a notice on http://railsplugins.org/
I backport some of the useful ActiveSupport features from Rails 3 into existing Rails 2 apps.