no block given yield - ruby-on-rails

So executing this gives me back an error:
no block given (yield)
Well never had a deep look at blocks in ruby, which seems to be an issue in here. If you have a better solution please provider, otherwise I wanted to find a workaround for the this legacy code...
def tab_groupings
result = at_a_glance_grouping
result += rating_grouping if #domain_context.include_ratings and (controller.controller_name !='rewards_credit_cards')
result += specific_tab_groupings
result
end
def at_a_glance_grouping
result = [[:at_a_glance, yield]]
product_type = controller.controller_name == 'fairfax' ? #product_type_helper[:controller] : controller.controller_name
result[0][1].insert(0, :overall_rating) if #domain_context.include_ratings and (product_type !='rewards_credit_cards')
result
end

yield is used to execute a block that you pass to the method, and then you do something with the result of that call.
Your method at_a_glance_grouping therefore expects you to pass a block to it... which it will then execute on the following line (where you use yield)
You don't pass any blocks to at_a_glance_grouping in the first line of tab_groupings, and therefore ruby rightfully complains.
What are you trying to achieve with the yield ?
Do you really need it at all?
If not - then just remove it.
If sometimes you do pass a block to this method, then you need to check for that before calling yield eg:
result = [[:at_a_glance, yield]] if block_given?

Related

How to defer execution of expensive create_by option

The following question is almost exactly what I need: https://stackoverflow.com/a/2394783/456188
I'd like to run the following:
find_or_create_by_person_id(:person_id => 10, :some_other => expensive_query)
But more importantly, I'd like to defer the execution of expensive_query unless I actually have to create the object.
Is that possible with find_or_create_by?
Turns out find_or_create_by* accepts a block that only gets run in the create case.
find_or_create_by_person_id(10) do |item|
item.some_other = expensive_query
end

Rails code segment explained

I am working on an app which has the below code:
def app
#app ||= begin
if !::File.exist? options[:config]
abort "configuration #{options[:config]} not found"
end
app, myoptions = Rack::Builder.parse_file(self.options[:config], opt_parser)
self.myoptions.merge! myoptions
app
end
end
I am struggling to get my head around several parts of it..
#app||= begin...end
Does this mean that if #app does not exist the block is run?
app ,options = rack::builder
What does the comma do to it?
Please help
Your first assumptions was correct, it does say that if #app is nil, set it to whatever is returned in the block delimited with begin, end.
Regarding the comma, it works like this:
avar, bvar = "atest", "btest"
If you look at the source for Rack:Builder.parse_file then you will notice the last line
return app, options
So it is returning two values.
Hope that helps
#Craig-Taub ansewered the question,
I just want to add some notes:
Ruby commands are expressions which means they return value and you can assign them to variables.
You can read more about expressions and statements on Wikipedia and PragProg.
Second is that when you return more than one value in a code block, Ruby will wrap it into a simple array and return it to the caller.
That's why it works like that.

URL manipulations in views

I have a code which generates a URL which is like this: #{URI.escape p.url(checkout_path)}
Now I need to check a condition where, if #{URI.escape p.url(checkout_path)} generates a URL = "http://mywebsite.com" then append /trunk to the end so it must be "http://mywebsite.com/trunk" else if it already has /trunk appended from before then it should be "http://mywebsite.com".
Thus finally if http://mywebsite.com then http://mywebsite.com/trunk
elsif
http://mywebsite.com/trunk then http://mywebsite.com
But I want to know how to do it using #{URI.escape p.url(checkout_path)}
I would throw this in a helper method somewhere, but you could effectively do something like this:
URI.escape(p.url(checkout_path)) =~ /\/trunk$/ ? URI.escape(p.url(checkout_path)).gsub('/trunk', '') : "#{URI.escape(p.url(checkout_path))}/trunk"

find_or_create and race-condition in rails, theory and production

Hi I've this piece of code
class Place < ActiveRecord::Base
def self.find_or_create_by_latlon(lat, lon)
place_id = call_external_webapi
result = Place.where(:place_id => place_id).limit(1)
result = Place.create(:place_id => place_id, ... ) if result.empty? #!
result
end
end
Then I'd like to do in another model or controller
p = Post.new
p.place = Place.find_or_create_by_latlon(XXXXX, YYYYY) # race-condition
p.save
But Place.find_or_create_by_latlon takes too much time to get the data if the action executed is create and sometimes in production p.place is nil.
How can I force to wait for the response before execute p.save ?
thanks for your advices
You're right that this is a race condition and it can often be triggered by people who double click submit buttons on forms. What you might do is loop back if you encounter an error.
result = Place.find_by_place_id(...) ||
Place.create(...) ||
Place.find_by_place_id(...)
There are more elegant ways of doing this, but the basic method is here.
I had to deal with a similar problem. In our backend a user is is created from a token if the user doesn't exist. AFTER a user record is already created, a slow API call gets sent to update the users information.
def self.find_or_create_by_facebook_id(facebook_id)
User.find_by_facebook_id(facebook_id) || User.create(facebook_id: facebook_id)
rescue ActiveRecord::RecordNotUnique => e
User.find_by_facebook_id(facebook_id)
end
def self.find_by_token(token)
facebook_id = get_facebook_id_from_token(token)
user = User.find_or_create_by_facebook_id(facebook_id)
if user.unregistered?
user.update_profile_from_facebook
user.mark_as_registered
user.save
end
return user
end
The step of the strategy is to first remove the slow API call (in my case update_profile_from_facebook) from the create method. Because the operation takes so long, you are significantly increasing the chance of duplicate insert operations when you include the operation as part of the call to create.
The second step is to add a unique constraint to your database column to ensure duplicates aren't created.
The final step is to create a function that will catch the RecordNotUnique exception in the rare case where duplicate insert operations are sent to the database.
This may not be the most elegant solution but it worked for us.
I hit this inside a sidekick job that retries and gets the error repeatedly and eventually clears itself. The best explanation I've found is on a blog post here. The gist is that postgres keeps an internally stored value for incrementing the primary key that gets messed up somehow. This rings true for me because I'm setting the primary key and not just using an incremented value so that's likely how this cropped up. The solution from the comments in the link above appears to be to call ActiveRecord::Base.connection.reset_pk_sequence!(table_name) This cleared up the issue for me.
begin
result = Place.where(:place_id => place_id).limit(1)
result = Place.create(:place_id => place_id, ... ) if result.empty? #!
rescue ActiveRecord::StatementInvalid => error
#save_retry_count = (#save_retry_count || 1)
ActiveRecord::Base.connection.reset_pk_sequence!(:place)
retry if( (#save_retry_count -= 1) >= 0 )
raise error
end

Use a function in a conditions hash

I'm building a conditions hash to run a query but I'm having a problem with one specific case:
conditions2 = ['extract(year from signature_date) = ?', params[:year].to_i] unless params[:year].blank?
conditions[:country_id] = COUNTRIES.select{|c| c.geography_id == params[:geographies]} unless params[:geographies].blank?
conditions[:category_id] = CATEGORY_CHILDREN[params[:categories].to_i] unless params[:categories].blank?
conditions[:country_id] = params[:countries] unless params[:countries].blank?
conditions['extract(year from signature_date)'] = params[:year].to_i unless params[:year].blank?
But the last line breaks everything, as it gets interpreted as follows:
AND ("negotiations"."extract(year from signature_date)" = 2010
Is there a way to avoid that "negotiations"." is prepended to my condition?
thank you,
P.
For something like this, you'll probably have to write your own SQL with find_by_sql. Still wrap it in a method in your model so your model's friends can access it nicely.

Resources