Call a method from constructed string in Ruby / Rails - ruby-on-rails

I've got a problem in doing some metaprogramming in Ruby / Rails which must be minor, but I can't get the clue.
I wan't to assign values to an active record relation, with my model having attributes:
MyModelClass.p1_id,
.p2_id,
...
.p8_id
SecondModel.position #Integer in (1..8)
I now want to do the following
sms = SecondModel.where(:xyz => 'bla')
sms.each do |sm|
mmc = MyModellClass.first
mmc.#somehow construct method here = sm.id
end
So that somehow this is accomplished
mmc.p1_id = sm.id
mmc.p2_id = sm.id
..
mmc.p8_id = sm.id
To sum up: I want to create that p*n*_id stuff dynamically, but I can't find out, how to tell Ruby, that this should be a method. I tried so far:
mmc.send('p#{sm.position.to_s}_id'.to_sym) = sm.id
But this doesn't work. Any clues?

You were close. Try this:
mmc.send("p#{sm.position.to_s}_id=", sm.id)
Here we call the method with = and pass the value of attribute as the second argument of send

Related

ruby on rails select a variable name using loop

There's a similar answer but it won't apply to me, but maybe will help someone: here
So, I have a loop and need to input a value into each of 20 variables, called product1, product2, ... , product20.
Im using nokogiri to change the values from a page, and manually it works:
li.content = #site.product1
li = #doc.css('li')[1]
But to avoid code repetition and also I have more cases like that one in my app im trying to make a loop, but it won't work since now.
What it need to do:
(1..20).each do |i|
li = #doc.css('li')[i]
li.content = #site.producti
end
Thanks
TRy
(0...20).each do |i|
li = #doc.css('li')[i]
li.content = #site.send("product#{i}")
end

Using index value in method

In my Rails application, in a model, I am trying to use the loop index x in the following method, and I can't figure out how to get the value:
def set_winners ## loops over 4 quarters
1.upto(4) do |x|
qtr_[x]_winner.winner = 1
qtr_[x]_winner.save
end
end
I'm going to keep searching but any help would be greatly appreciated!
edit: So I guess I can't do that! Here is the original method I was trying to refactor in full by looping four times:
def set_winners
## set all 4 quarter's winning squares
home_qtr_1 = game.home_q1_score.to_s.split('').last.to_i
away_qtr_1 = game.away_q1_score.to_s.split('').last.to_i
qtr_1_winner = squares.where(xvalue:home_qtr_1, yvalue:away_qtr_1).first
qtr_1_winner.winner = 1
qtr_1_winner.save
home_qtr_2 = game.home_q2_score.to_s.split('').last.to_i
away_qtr_2 = game.away_q2_score.to_s.split('').last.to_i
qtr_2_winner = squares.where(xvalue:home_qtr_2, yvalue:away_qtr_2).first
qtr_2_winner.winner = 1
qtr_2_winner.save
home_qtr_3 = game.home_q3_score.to_s.split('').last.to_i
away_qtr_3 = game.away_q3_score.to_s.split('').last.to_i
qtr_3_winner = squares.where(xvalue:home_qtr_3, yvalue:away_qtr_3).first
qtr_3_winner.winner = 1
qtr_3_winner.save
home_qtr_4 = game.home_q4_score.to_s.split('').last.to_i
away_qtr_4 = game.away_q4_score.to_s.split('').last.to_i
qtr_4_winner = squares.where(xvalue:home_qtr_4, yvalue:away_qtr_4).first
qtr_4_winner.winner = 1
qtr_4_winner.save
end
Is there a better way to do this if it's bad practice to dynamically change attribute names?
It looks like you are trying to do a PHP-like trick in a language that doesn't support it, and where we recommend NOT doing it because it results in code that is very difficult to debug due to the dynamically named variables.
It looks like you want to generate a variable name using:
qtr_[x]_winner
to create something like:
qtr_1_winner
Instead, consider creating an array named qtr_winner containing your objects and access the elements like:
qtr_winner[1]
or
qtr_winner[2]
etc.
You could create a hash to do a similar thing:
qtr_winner = {}
qtr_winner[1] = 5
then later access it using qtr_winner[1] and get 5 back or
qtr_winner[1].winner = 1
The determination of whether to use a hash or an array is whether you need to walk the container, or need random access. If you are always indexing into it using a value, then it's probably a wash about which is faster.
Based on your edit, you don't need dynamic variables. The only thing that changes in your loop is game.home_qN_score, so that's what the focus of your refactoring should be. Given that, here's a viable solution:
1.upto(4) do |i|
home_qtr = game.send("home_q#{i}_score)".to_s.split('').last.to_i
away_qtr = game.send("away_q#{i}_score)".to_s.split('').last.to_i
winner = squares.where(xvalue:home_qtr, yvalue:away_qtr).first
winner.winner = 1
winner.save
end
Original answer:
If qtr_1_winner, etc. are instance methods, you can use Object#send to achieve what you want:
def set_winners ## loops over 4 quarters
1.upto(4) do |x|
send("qtr_#{x}_winner").winner = 1
send("qtr_#{x}_winner").save
end
end

Extract params values from link, Rails 4

I have link like this:
http://localhost:3000/sms/receive/sms-id=7bb28e244189f2cf36cbebb9d1d4d02001da53ab&operator-%20id=1&from=37126300682&to=371144&text=RV9+c+Dace+Reituma+0580913
I want to extract all diferent variable values from this link. For example sms-id,operator,from, to and text.
So far I have like this:
routes.rb
get 'sms/receive/:params', to: 'sms#receive'
SMS#RECEIVE controller
def receive
query = params[:params]
sms_id= query[/["="].+?[&]/]
flash[:notice] = sms_id
end
This gives me : =7bb28e244189f2cf36cbebb9d1d4d02001da53ab& but I need without the first = and last characher & .
If I try to add strings like :query[/["sms-id"].+?[&operator]/] that could allow me to extract all variables smoothly, it gives me error : empty range in char class: /["sms-id"].+?[&operator]/
But I believe there is other way to extract all these variable values in different way?
Thanks in advance!
The error in your regular expression is because the - is a reserved character when in-between square brackets. In this context, it must be escaped with a backslash: \-.
To parse your query string, you can do this:
sms_id = params[:params].match(/sms-id=([^&]*)/)[1]
or parse it with the more generic method:
parsed_query = Rack::Utils.parse_nested_query(params[:params])
sms_id = parsed_query['sms-id']
(quoted from this answer)
If you have control over the initial URL, change the last / for a ? for an even easier solution:
http://localhost:3000/sms/receive?sms-id=7bb28e244189f2cf36cbebb9d1d4d02001da53ab&operator-%20id=1&from=37126300682&to=371144&text=RV9+c+Dace+Reituma+0580913
and you will have sms-id in params:
sms_id = params['sms-id']
You need
get 'sms/receive/', to: 'sms#receive'
path in routes.rb and get params in the controller
Try this
matches = params[:params].scan(/(?:=)([\w\+]+)(?:\&)?/)
# this will make matches = [[first_match], [second_match], ..., [nth_match]]
# now you can read all matches
sms_id = matches[0][0]
operator_id = matches[1][0]
from = matches[2][0]
to = matches[3][0]
text = matches[4][0]
# and it will not contatin = or &
I suggest for you to make method in model or helper, and not to write whole code in controller.

undefined method `gsub' for nil:NilClass

I'm newvbie in ruby on rails.. I'm having problem with gsub.. I everytime I go to the list of my store page it says "undefined method `gsub' for nil:NilClass"..
here is mycode :
def self.search(search_val, page = 1)
#search_val = search_val.gsub("'", "\\\\'")
search_query = "store_id LIKE '%#{ #search_val }%' OR english_name LIKE '%#{ #search_val }%' OR chinese_name LIKE '%#{ #search_val }%'"
select("jos_store.id, store_id, english_name, chinese_name, store_manager, delivery_area,year, week").joins("LEFT OUTER JOIN (SELECT id as store_replenishment, store, MAX(stock_movement) AS stock_movement FROM jos_store_replenishment GROUP BY store) AS replenishment ON replenishment.store = jos_store.id").joins("LEFT OUTER JOIN jos_stock_movement ON jos_stock_movement.id = replenishment.stock_movement").where(search_query).order("year DESC, week DESC").paginate :page => page, :per_page => 15
end
thanks in advance
A good practice is doing .to_s when you are using string methods.
You can use the & operator on search_val. It allows you to avoid null pointer exceptions without adding additional checks or using to_s to convert a string to a string.
So, you'll have something like this:
#search_val = search_val&.gsub("'", "\\\\'")
You can read more on the safe navigation operator here: http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/
This means that search_val is in fact nil. You can easily verify this by printing out the value of search_val.
I'm not sure if this is your case, but the same undefined method gsub for nil:NilClass error happened with me after a few rollbacks and migrations.
Then, I restarted the server and works. Maybe this could be the case for some people that reached this topic searching on Google.

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