Memcache basic use in rails 3 - ruby-on-rails

So I'm trying to learn how to use memcache. I installed it in my system. I'm running it. I installed the dalli gem.
All that seems to be just fine.
Lets say I'd like to cache my users table.
I put this in my User.rb file:
def self.all_cached
Rails.cache.fetch('User.all') { all }
end
Then in my controller file, I used to have:
#users = User.where(:group_id => current_user.group_id)
So now I'd like to have something like:
#users = User.all_cached.where(:group_id => current_user.group_id)
I'm getting a no method error for where... Any ideas for how I should accomplish this?

Based on your comment there, I take it you are getting an error like:
undefined method `where' for #<Array:0x00000004d92520>
That's because where works on a model, but when you do User.all, it returns basically an array, and there is no where method defined for an array.
You may want to use the find_all method for enumerables (and arrays) instead (as seen here: http://www.ruby-doc.org/core/classes/Enumerable.html#M001484), or even try a different approach all together. That's your choice.
Here is the example they give to give you an idea off the bat of how it would work:
(1..10).find_all {|i| i % 3 == 0 } #=> [3, 6, 9]

Related

Batch search for named_scope

This is an existing code written by someone else and am trying to enhance it. I am a java developer working on Ruby on Rails, so kindly be considerate.
I have entities like this
User
Delivery entity,
Delivery
belongs_to :user
named_scope :for_abcs, :conditions => {'deliveries.xyz_type' => ['Xyz1', 'Xyz2']},
many such named-scopes are defined.
Now to fetch the deliveries its written like this
#deliveries = current_user.deliveries.send("for_abcs").with(:xyz, :sender, :receiver)
...
...
...
# few other conditions added to #deliveries
finally
#deliveries.sort(...)
This sort is taking huge sql and giving performance issues. I want to use find_each, but find_each is only for Active Entity in Ruby on Rails, How can I achieve this (if possible) without much code change)
Earlier I used to do
Delevery.find_each
wherever it is
Delivery.find
Now I cant do as it is an array, what is the workaround or right procedure to do that in Ruby on Rails.
EDIT :
What I tried :
deliveries_temp = []
#deliveries.find_each(:batch_size=>999) do |delivery_temp|
deliveries_temp.push(delivery_temp)
end
This gave me error
undefined method `find_each' for []:Array
type(#deliveries) returned ActiveRecord::NamedScope::Scope , rails version 2.3.18
find_each should work on anything that returns a Relation (which includes scopes).
#deliveries = current_user.deliveries.for_abcs(:xyz, :sender, :receiver).find_each
Update
It sounds like you're using Rails 2.3. find_each is a class method in 2.3, so you'll need a way to extract the conditions from your scope and pass them to find_each. I found an article that looks promising, so give this a try:
Delivery.find_each(current_user.deliveries.for_abcs.scope(:find))
Also, I'm still not sure what that #with is doing. Maybe it's supposed to be #includes?
After lot of research for a week and learning about named_scopes by checking its source code. I understood what the problem was. The #deliveries is an object of class ActiveRecord::NamedScope::Scope . This class do not have find_each method. So I wrote a new named_scope for limit and offset in Delivery model file as follows :
named_scope :limit_and_offset, lambda { |lim,off| { :limit => lim, :offset=>off } }
After this , I called it in a loop passing offset and limit , for ex. first loop has offset=0, limit=999 , second loop has offset=999, limit=999 . I will add all the results into an emptry array. This loop continues till the result size is less than the limit value . This is working exactly the way I wanted , in batches.
set = 1
total_deliveries = []
set_limit=999
original_condition = #deliveries
loop do
offset = (set-1) * set_limit
temp_condition = original_condition.limit_and_offset(set_limit,offset)
temp_deliveries = temp_condition.find(:all)
total_deliveries+= temp_deliveries
set += 1
break if temp_deliveries.size < set_limit
end
#deliveries = total_deliveries.sort do |a, b|

Ruby method that returns itself

I am doing some reflection, and ran into an unexpected road block.
Is there an object method in ruby (or rails) that returns itself
ruby-1.9.2> o = Object.new
=> #<Object:0x00000104750710>
ruby-1.9.2> o.class
=> Object
ruby-1.9.2> o.send :self
NoMethodError: undefined method `self' for #<Object:0x00000104750710>
What I want
ruby-1.9.2> o.send :self
=> #<Object:0x00000104750710>
Is this built in? Or do I need to extend Object (It always gets me nervous opening up Object):
class Object
def itself
self
end
end
And then so:
ruby-1.9.2> o.send :itself
=> #<Object:0x00000104750710>
Ok, some background on what I am trying to achieve. I have a generic table helper in my rails app, and you call if like so:
render_list #person, [{field: :name, link_to: :itself},
{field: {address: :name}, link_to: :address}]
I was struggling on the right way to call :itself -- but i'm thinking that my patch is the way to go.
Yes! If you have Ruby 2.2.0 or later, you can use the Kernel#itself method.
You can see the extensive discussion of this feature here: https://bugs.ruby-lang.org/issues/6373. The patch was submitted by Rafael França in message #53.
You can see it in the official Ruby source by looking in object.c.
There is a discussion about adding such method: http://bugs.ruby-lang.org/issues/6373
If you are using Ruby version >= 1.9 you can use tap method with empty block:
Object.tap{} => Object
Object.new.tap{} => #<Object:0x5f41334>
self is the object itself, no need to extra fetch it. After your patch, try the following:
>> a=[2,3,4] #=> [2, 3, 4]
>> a == a.itself #=> true
>> a.object_id #=> 71056290
>> a.itself.object_id #=> 71056290
...it is exactly the same
self is a keyword referring to the default receiver. It is not a method. See this page for an example.
Your itself method works fine. You can also say:
o.instance_eval('self')
For a class, use class_eval instead:
Object.class_eval('self')
There is a #yourself method in Smalltalk. It has sense because of the syntax of the language where you can send several messages to the same object and want to get the object itself at the end of the phrase.
aList add: (anObjet doThis; andThat; yourself).
Also in Smalltalk the default return value for a method is self, but in Ruby it's the last instruction's return value (or nil if there is nothing in the method).
Anyway maybe we should all start using explicit returns :)
If for some weird logic reason you have to call a method on some object but what you want is really the object itself, then I don't see why you couldn't extend the Object class to do just that.
There's really no reason why it would break your program unless the method exists somewhere else (did or will exist) and did (or will) do something else. Maybe a slight loss in performance?
Try .presence
>> a=[2,3,4]
=> [2, 3, 4]
>> a == a.presence
=> true

Rails 3 - Undefined method on certain collections

I have a search method written for my model Link.
I've been able to called this method without error until implementing voting. For example, these all work:
Link.search(params[:search])
current_user.links.search(params[:search])
current_account.links.search(params[:search])
The following does not work:
#links = current_user.votes.collect {|vote| vote.voteable}
#favorites = #links.search(params[:search])
and return this error:
undefined method `search' for #<Array:0x00000006919ac8>
I've done some testing, to see if my class is wrong, in the console:
links = user.votes.map {|vote| vote.voteable}
links.class
=> Array
links.first.class
=> Link
This should be no different than my working examples:
user.links.class
=> Array
user.links.first.class
=> Link
I thought maybe the error was from me calling search on an array and not a link. But in previous examples I'm also calling it on an array.
I'm using vote_fu to handle the voting thus the vote/voteable.
The search function or scope that you have defined is defined on the Link object and is usable in Link relations, but it is not defined on a simple array, which is what is getting returned from the first collect example. Here is a simple distinction:
class User
scope :search, lambda{ |name| where(name: name) }
end
User.search('Kombo').all # Returns an array of the SQL result run against the DB
User.all.search('Kombo') # NoMethodError: undefined method `search' for #<Array:0x000001079b15b0>
In your first example, Link.search(params[:search]), you are performing the equivalent of User.search.all, and User is a scoped ActiveRecord relation/object, which means it can continue to be combined with other scopes, like where, limit and group. In the second example, #links = current_user.votes.collect {|vote| vote.voteable}, collect is acting on such a relation and is returning a simple array which can no longer be acted upon with these scoped functions. The second example is like doing User.all.search.
It's confusing because both of these examples resolve to an Array eventually, but the difference is what is happening before that resolution to an Array, and when you are actually calling the search function. To get around this you'll have to actually call the search scope or function on an ActiveRecord object, like Link or an ActiveRecord Relation like current_user.links, but you won't be able to call it on a result. Just to clarify:
Link.search(params[:search]) # will work
Link.all.search(params[:search]) # will not work
current_user.links.search(params[:search]) # will work
current_user.links.all.search(params[:search]) # will not work
current_account.links.search(params[:search]) # will work
current_account.links.all.search(params[:search]) # will not work
When you call .collect you are implicitly calling .all, which breaks the scope chain. The following two commands are equivalent in that respect:
#links = current_user.votes.collect {|vote| vote.voteable}
#links = current_user.votes.all.collect {|vote| vote.voteable}

Rails: any way to refactor this ActiveRecord code?

I have a piece of code that checks that a survey response picked by user to a survey question is in fact one of valid choices:
Question.find_by_id(question_id).question_choices.all(:select => 'id').map {|x| x.id}.include?(user_choice_id)
Is there an easier way?
Thanks!
At the very least the question_choices.all(:select => 'id').map {|x| x.id} component can be rewritten, as ActiveRecord provides a method for this question_choice_ids.
You can also just do find instead of find_by_id. I know that find will raise an exception if nothing is found, but so will calling question_choices on nil in your example, anyway:
Question.find(question_id).question_choice_ids.include?(user_choice_id)
# or
# Rails 2 (will run 2 queries unless you use :include)
Question.find(question_id).question_choices.first(:conditions => {:id => user_choice_id})
# Rails 3 (will only run 1 query)
Question.find(question_id).question_choices.where(:id => user_choice_id).first

Sending a code block to a find_all dynamic method

I am working with some complex queries using the dynamic find_all method and reached to a point where sending a block to that find_all method would really simplify my code.
Is there any plugin or work in-progress dealing with this?
In simple terms, I'd like to do something like:
#products = Product.find_all_by_ids(ids, .....) do |p|
# do something to each product like
p.stock += 10
end
Any other guide or better way of doing this would be greatly appreciated.
Rails 2.3 introduced the find_in_batches and find_each methods (see here) for batch processing of many records.
You can thus do stuff like:
Person.find_each(:conditions => "age > 21") do |person|
person.party_all_night!
end
I use the .each method which Enumerable provides like
#products = Product.find_all_by_ids(ids, .....)
#products.each { |p| p.stock += 10 }
There are even some extensions to Enumerable that Rails provides that might help you a bit if you're doing some common stuff.
Also, don't forget to save your objects with something like p.save if you want the changes to actually persist.
What's wrong with this:
#products = Product.find_all_by_ids(ids).each do |p|
p.stock+=10
end
In case you didn't know, each returns the array passed to it.

Resources