Here’s an interesting fact when checking if objects or relations exist in a collection.
To check if there were any items present in a collection you can do something like this:
Object.relation.present?
This, however, is better:
Object.relation.any?
Turns out that - when you request associated objects for the first time - the any? method will perform a COUNT (*) SQL query where as the present? method will perform a SELECT (*) which is infinitely slower than performing a count.
blog = Blog.first
blog.posts.present?
# SQL (284.1ms) SELECT * FROM "posts" WHERE ("posts".blog_id = 1)
blog = Blog.first
blog.posts.any?
# SQL (1.2ms) SELECT count(*) AS count_all FROM "posts" WHERE ("posts".blog_id = 1)I am sure that we have all had to track how long some bit of code takes to process. For auditing purposes or whatever. The typically way we do this is to save the time just before the execution of our action, perform the action, and then get the time when the action completes. End time - Start time = Elapsed time. Hurrah! Look at us being geniuses….
And now for the “bring you back to earth because we did something the not so Rails (or rather Ruby) way” moment. Rails, in it’s sneakiness has extended the Benchmark class and given us a method called #ms. This method receives a block of code and gives the elapsed time for a return result. Did you catch that? Did you really? Because I said the #ms functions return result is the elapsed time. Not the result of the block. This, if you aren’t keeping it in mind can cause a problem.
Let’s say you want to see how long it takes to look up a person in your DB (simple but gets the point across). You might start out by doing something like this:
ms_murder = User.find(:first, :conditions => { :some => "condition" })
Then you think to yourself “I wonder how long that takes”. So you now know about the Benchmark#ms function to you just wrap your previous code like so
Benchmark.ms do
ms_murder = User.find(:first, :conditions => { :some => "condition" })
end
#=> 0.33299827575684
Great! now you know how long it takes. But wait. What happens when you try to use your ms_murder object?
ms_murder.login
#=> NameError: undefined local variable or method `ms_murder' for #<Object:0x1001972a0>
Wah wah. The problem here should be obvious but just to clarify the variables that are defined within the Benchmark#ms block are not available outside of that. There are a few ways around this. The first is to initialize the vars before the code block, blech. The second is to use instance variables, warm and fuzzy. I’m sure there are other more creative ways but I have found this works for most of my needs:
Benchmark.ms do
@ms_murder = User.find(:first, :conditions => { :some => "condition" })
end
#=> 0.33299827575684
@ms_murder.login
#=> "jerrol"
You can also do this if you need to keep the elapsed time around for whatever reason.
elapsed_time = Benchmark.ms do
@ms_murder = User.find(:first, :conditions => { :some => "condition" })
end
#=> 0.33299827575684
@ms_murder.login
#=> "jerrol"
elapsed_time
#=> 0.33299827575684
9 notes 0 comments (via schmidt-happens)
Need a randomly chosen element from an array? There’s a method for that!
Array#choice (Ruby 1.8.7) or Array#sample (Ruby > 1.8.7):
[1,2,3,4,5,6,7,8,9].choice => 5 [1,2,3,4,5,6,7,8,9].sample => 8 [1,2,3,4,5,6,7,8,9].sample(3) => [3,8,9]
Thanks to everyone who mentioned these methods here and here.
Calling private methods can for example be useful in unit testing to increase the code coverage.
Object#send gives you access to all methods of a particular object (even protected and private ones).
obj.send(:method [, args...])
In case send method has been overwritten, you can also use its aliased version __send__.
11 notes 0 comments (via rubyloveinfo)
Here’s a method I haven’t seen before: attr_accessor_with_default
This ActiveSupport method allows you to set a default value for an attribute accessor:
class Person attr_accessor_with_default :age, 25 end some_person.age # => 25 some_person.age = 26 some_person.age # => 26
You can even pass in a block.
20 notes 0 comments (via rubyflare)
From 1.8.7 on, there is the Array#shuffle method.
[1,2,3].shuffle # => [2,1,3]
This makes it extremely easy to write Array#random to pick a random item from an array
class Array def random shuffle.first end end [1,2,3,4,5,6,7,8,9].random # => 5 [1,2,3,4,5,6,7,8,9].random # => 1 [1,2,3,4,5,6,7,8,9].random # => 3
This tip was submitted by Justin Baker.
The default behavior of String#split will throw away any trailing values if they are empty.
> "Hello,There,,".split(',')
=> ["Hello", "There"]
If you want to keep those empty trailing elements, pass a negative number for the second (limit) parameter.
> "Hello,There,,".split(',', -1)
=> ["Hello", "There", "", ""]
This tip was submitted by two-bit-fool.
There’s a Prototype helper method called delay, so you can more precisely say when something will happen.
For example, you can do stuff like this inside your RJS files:
page["old_element"].visual_effect :blind_up, :duration => 0.5
page.delay(0.5) do
page.replace :old_element, :partial => "new_element"
page["new_element"].visual_effect :blind_down
end
Check out the delay method.
Did you know you can pass a number parameter to Array#first and Array#last?
x = [1,2,3,4,5,6,7,8,9,10]
x.first 5
=> [1,2,3,4,5]
x.last 2
=> [9,10]
This tip was submitted by Alfred Nagy.
You know you can access the 42nd element of an Array like this:
my_array[41]
In Rails, you can also access this element with the forty_two method:
my_array.forty_two
Check out the Array#forty_two method.
Your number has too many zeros? In ruby you can make that more readable (and easier to write!) by using underscores:
moneys = 1_000_000.00
=> 1000000.0
7 notes 0 comments (via rubyloveinfo)
Coming from java - from time to time it just has to be… “copy-paste-time”. You’re used to it:
puts "response.inspect: #{response.inspect}"
puts "response.error_type: #{response.error_type}"
puts "response.response: #{response.response}"
puts "response.body: #{response.body}"
But wait - this is Ruby! Let’s have some fun with that spell I read about: Dynamic Dispatching. Let’s define a method that takes the object and the method to be called:
def show_response_method(method_to_call, response)
puts "response.#{method_to_call}: #{response.send(method_to_call)}"
end
Now, we don’t have to repeat the textual output all the time:
show_response_method :inspect, response
show_response_method :error_type, response
show_response_method :response, response
show_response_method :body, response
And, in a more Ruby-like manner, we can bum some lines by using this little fellow:
[:inspect, :error_type, :response, :body].each { |method| show_response_method method, response }
We could furthermore use the Open Class concept and get funky with a little Monkey Patching and inject this method into the response object itself - but this would be one of the cases where monkey patching is not the right thing to do. At least, this is my opinion. What do you think about it?
This tip was submitted by 5v3n.
Being from c programming background, to get an array of some property from the objects, I used to write this in Ruby:
amount_array = []
for order in account.orders
amount_array << order.amount.some_operation
end
While a much cleaner way is to use Array#collect:
amount_array = account.orders.collect { |order| order.amount.some_operation }
This tip was submitted by zerothabhishek.
Every rails app I’ve ever built needs some sort of configuration, and I seem to be solving this problem a different way every time, which really bothers me. Today I learned about a new class called OpenStruct. Here’s how you could use it.
# in app_root/config/initializers/app_config.rb
require 'ostruct'
AppConfig = OpenStruct.new
AppConfig.default_email = "no-reply@example.com"
AppConfig.api_url = "staging.someapi.com"
Now you have a neat way of defining global configuration variables without using constants or defining custom classes.
This tip was submitted by Jon Druse.
Ruby comes with an Object#clone method that lets you copy objects. But this method makes a shallow copy, i.e. a duplicate without copying any referenced objects.
Produces a shallow copy of obj - the instance variables of obj are copied, but not the objects they reference.
If you need a deep clone of an object - i.e. a copy including referenced objects - the Marshal module is your friend:
deep_cloned = Marshal::load(Marshal.dump(origin))
This tip was submitted by Jaime Iniesta.