Use a function in a conditions hash - ruby-on-rails

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.

Related

cyclomatic complexity is too high rubocop for method

This is code I have using in my project.
Please suggest some optimizations (I have refactored this code a lot but I can't think of any progress further to optimize it )
def convert_uuid_to_emails(user_payload)
return unless (user_payload[:target] == 'ticket' or user_payload[:target] == 'change')
action_data = user_payload[:actions]
action_data.each do |data|
is_add_project = data[:name] == 'add_fr_project'
is_task = data[:name] == 'add_fr_task'
next unless (is_add_project or is_task)
has_reporter_uuid = is_task && Va::Action::USER_TYPES.exclude?(data[:reporter_uuid])
user_uuids = data[:user_uuids] || []
user_uuids << data[:owner_uuid] if Va::Action::USER_TYPES.exclude?(data[:owner_uuid])
user_uuids << data[:reporter_uuid] if has_reporter_uuid
users_data = current_account.authorizations.includes(:user).where(uid: user_uuids).each_with_object({}) { |a, o| o[a.uid] = {uuid: a.uid, user_id: a.user.id, user_name: a.user.name} }
if Va::Action::USER_TYPES.include? data[:owner_uuid]
data['owner_details'] = {}
else
data['owner_details'] = users_data[data[:owner_uuid]]
users_data.delete(data[:owner_uuid])
end
data['reporter_details'] = has_reporter_uuid ? users_data[data[:reporter_uuid]] : {}
data['user_details'] = users_data.values
end
end
Note that Rubocop is complaining that your code is too hard to understand, not that it won't work correctly. The method is called convert_uuid_to_emails, but it doesn't just do that:
validates payload is one of two types
filters the items in the payload by two other types
determines the presence of various user roles in the input
shove all the found user UUIDs into an array
convert the UUIDs into users by looking them up
find them again in the array to enrich the various types of user details in the payload
This comes down to a big violation of the SRP (single responsibility principle), not to mention that it is a method that might surprise the caller with its unexpected list of side effects.
Obviously, all of these steps still need to be done, just not all in the same method.
Consider breaking these steps out into separate methods that you can compose into an enrich_payload_data method that works at a higher level of abstraction, keeping the details of how each part works local to each method. I would probably create a method that takes a UUID and converts it to a user, which can be called each time you need to look up a UUID to get the user details, as this doesn't appear to be role-specific.
The booleans is_task, is_add_project, and has_reporter_uuid are just intermediate variables that clutter up the code, and you probably won't need them if you break it down into smaller methods.

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

Is there a safe way to Eval In ruby? Or a better way to do this?

When a user uses my application, at one point they will get an array of arrays, that looks like this:
results = [["value",25], ["value2",30]...]
The sub arrays could be larger, and will be in a similar format. I want to allow my users to write their own custom transform function that will take an array of arrays, and return either an array of arrays, a string, or a number. A function should look like this:
def user_transform_function(array_of_arrays)
# eval users code, only let them touch the array of arrays
end
Is there a safe way to sandbox this function and eval so a user could not try and execute malicious code? For example, no web callouts, not database callouts, and so on.
First, if you will use eval, it will never be safe. You can at least have a look in the direction of taint method.
What I would recommend is creating your own DSL for that. There is a great framework in Ruby: http://treetop.rubyforge.org/index.html. Of course, it will require some effort from your side, but from the user prospective I think it could be even better.
WARNING: I can not guarantee that this is truly safe!
You might be able to run it as a separate process and use ruby $SAFE, however this does not guarantee that what you get is safe, but it makes it harder to mess things up.
What you then would do is something like this:
script = "arr.map{|e| e+2}" #from the user.
require "json"
array = [1, 2, 3, 4]
begin
results = IO.popen("ruby -e 'require \"json\"; $SAFE=3; arr = JSON.parse(ARGV[0]); puts (#{script}).to_json' #{array.to_json}") do |io|
io.read
end
rescue Exception => e
puts "Ohh, good Sir/Mam, your script caused an error."
end
if results.include?("Insecure operation")
puts "Ohh, good Sir/Mam, you cannot do such a thing"
else
begin
a = JSON.parse(results)
results = a
rescue Exception => e
puts "Ohh, good Sir/Mam, something is wrong with the results."
puts results
end
end
conquer_the_world(results) if results.is_a?(Array)
do_not_conquer_the_world(results) unless results.is_a?(Array)
OR
You could do this, it appears:
def evaluate_user_script(script)
Thread.start {
$SAFE = 4
eval(script)
}
end
But again: I do not know how to get the data out of there.

Call a method from constructed string in Ruby / 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

Resources