Ruby send(attribute.to_sym) for Rails Method - ruby

I'm using Ruby 1.9.2 and need to go through all of the values for a table to make sure everything is in UTF-8 encoding. There are a lot of columns so I was hoping to be able to use the column_names method to loop through them all and encode the values to UTF-8. I thought this might work:
def self.make_utf
for listing in Listing.all
for column in Listing.column_names
column_value_utf = listing.send(column.to_sym).encode('UTF-8')
listing.send(column.to_sym) = column_value_utf
end
listing.save
end
return "Updated columns to UTF-8"
end
But it returns an error:
syntax error, unexpected '=', expecting keyword_end
listing.send(column.to_sym) = column_value_utf
I can't figure out how to make this work correctly.

You're using send wrong and you're sending the wrong symbol for what you want to do:
listing.send(column + '=', column_value_utf)
You're trying to call the x= method (for some x) with column_value_utf as an argument, that's what o.x = column_value_utf would normally do. So you need to build the right method name (just a string will do) and then send the arguments for that method in as arguments to send.

Related

Fix deprecation warning `Dangerous query method` on `.order`

I have a custom gem which creates a AR query with input that comes from an elasticsearch instance.
# record_ids: are the returned ids of the ES results
# order: is the order of the of the ids that ES returns
search_class.where(search_class.primary_key => record_ids).order(order)
Right now the implementation is that I build the order string directly into the order variable so it looks like this: ["\"positions\".\"id\" = 'fcdc924a-21da-440e-8d20-eec9a71321a7' DESC"]
This works fine but throws a deprecation warning which ultimately will not work in rails6.
DEPRECATION WARNING: Dangerous query method (method whose arguments are used as raw SQL) called with non-attribute argument(s): "\"positions\".\"id\" = 'fcdc924a-21da-440e-8d20-eec9a71321a7' DESC". Non-attribute arguments will be disallowed in Rails 6.0. This method should not be called with user-provided values, such as request parameters or model attributes. Known-safe values can be passed by wrapping them in Arel.sql()
So I tried couple of different approaches but all of them with no success.
order = ["\"positions\".\"id\" = 'fcdc924a-21da-440e-8d20-eec9a71321a7' DESC"]
# Does not work since order is an array
.order(Arel.sql(order))
# No errors but only returns an ActiveRecord_Relation
# on .inspect it returns `PG::SyntaxError: ERROR: syntax error at or near "["`
.order(Arel.sql("#{order}"))
# .to_sql: ORDER BY [\"\\\"positions\\\".\\\"id\\\" = 'fcdc924a-21da-440e-8d20-eec9a71321a7' DESC\"]"
order = ['fcdc924a-21da-440e-8d20-eec9a71321a7', ...]
# Won't work since its only for integer values
.order("idx(ARRAY#{order}, #{search_class.primary_key})")
# .to_sql ORDER BY idx(ARRAY[\"fcdc924a-21da-440e-8d20-eec9a71321a7\", ...], id)
# Only returns an ActiveRecord_Relation
# on .inspect it returns `PG::InFailedSqlTransaction: ERROR:`
.order("array_position(ARRAY#{order}, #{search_class.primary_key})")
# .to_sql : ORDER BY array_position(ARRAY[\"fcdc924a-21da-440e-8d20-eec9a71321a7\", ...], id)
I am sort of stuck since rails forces attribute arguments in the future and an has no option to opt out of this. Since the order is a code generated array and I have full control of the values I am curious how I can implement this. Maybe someone had this issue before an give some useful insight or idea?
You could try to apply Arel.sql to the elements of the array, that should work, ie
search_class.where(search_class.primary_key => record_ids)
.order(order.map {|i| i.is_a?(String) ? Arel.sql(i) : i})

A ruby SPARQL query that SELECT(s) all triples for a given subject

I'm using the following ruby library to query a SPARQL endpont.
http://ruby-rdf.github.io/sparql-client/
I'm really close to achieving what I want. With the following code I can get a print out of all the triples in my database.
sparql = SPARQL::Client.new("http://localhost:3030/ds/query")
query = sparql.select.where([:s, :p, :o]).offset(100).limit(1000)
query.each_solution do |solution|
puts solution.inspect
end
But now I want to change this just slightly and select all the triples for a given subject. I thought the following would work, but it doesn't.
sparql = SPARQL::Client.new("http://localhost:3030/ds/query")
itemname = "lectio1"
query = sparql.select.where(["<http://scta.info/items/#{itemname}>", :p, :o]).offset(100).limit(1000)
query.each_solution do |solution|
puts solution.inspect
end
This would work in a straightforward SPARQL syntax, but somehow replacing the symbol :s with the literal subject I want queried doesn't work. The error that Sinatra gives me is:
expected subject to be nil or a term, was "<http://scta.info/items/lectio1>";
As you noted in the comments,
The error that Sinatra gives me is: expected subject to be
nil or a term, was "<http://scta.info/items/lectio1>"
You're pasing the method a string, but it's expecting nil or a term. The kind of term that it's expecting is an RDF::Term. In your particular cases, you want a URI (which is a subclass of RDF::Resource, which is a subclass of RDF::Term). You can create the reference that you're looking for with
RDF::URI.new("http://scta.info/items/#{itemname}")
so you should be able to update your code to the following (and depending on your imports, you might be able to drop the RDF:: prefix):
sparql = SPARQL::Client.new("http://localhost:3030/ds/query")
itemname = "lectio1"
query = sparql.select.where([ RDF::URI.new("http://scta.info/items/#{itemname}"), :p, :o]).offset(100).limit(1000)
query.each_solution do |solution|
puts solution.inspect
end

How to modify the value of a variable whose name is given

I wish to have a method that takes in a string, and then updates a variable with the name of that string. This is an example of my attempt:
#other_class = OtherClass.new()
def change_variable(variable_string)
self.#other_class.send.variable_string += 1
end
I am getting the error:
syntax error, unexpected tIVAR
with the pointer just before 'send' in the method above. Does anyone have any suggestions to make this work?
You probably want instance_variable_set http://ruby-doc.org/core-2.0/Object.html#method-i-instance_variable_set
The syntax using your variables is I think:
other_var = ('#' + variable_string).to_sym
#other_class.instance_variable_set( other_var, #other_class.instance_variable_get( other_var ) + 1 )
The immediate syntatic error is that you're using self and # wrongly.
Either one is fine but not in conjunction with each other.
So in your case self.other_class.send... would be fine but then you cant declare it as #.
As would # be but then you cant do self.
These are meant to do different things and these are that
# is an instance variable and so is self but the difference is that using # calls the attribute other_class directly as to where self calls the method other_class.
So # is both a getter and setter in one so you can do
#other_class = my_milk_man as to where
self.other_class -> self.other_class (as getter),
self.other_class = my_milk_man -> self.other_class= (as setter).

How to pass special parameters in a function in Ruby Selenium Webdriver

I'm using Selenium Webdriver Ruby to create a function in order to wait for an element displayed within a Time that I set in this function
My function will have two parameters (one Optional parameter & one parameter is locator name of an object)
So, I have my function with two parameters like below:
def wait_for_element_displayed(locator, options={})
if options[:time]
settime = options[:time]
else
settime= 60
end
#driver.find_element(locator).click
end
So, I will call this function like: wait_for_element_displayed(:id=>"unique1", :time => "20")
However, I got the error when trying to calling this function with this way, the error is: "TypeError: can't convert String into an exact number".
Anybody please help me how to pass two parameters like my scenario to a function? Any help appreciated.
A few suggestions:
You can use hash#fetch to set a default value if the key you are looking up is not present:
time = options.fetch(:time) { 60 }
Ruby will have a hard time parsing your input when both arguments are hashes and you don't differentiate between them. Also, your error makes me think you should provide 20 as an integer instead of a string. Try calling the method like this:
wait_for_element_displayed({:id => "unique1"}, { :time => 20 })
Lastly, I don't know much about Selenium, so maybe someone can fill me in on the last point. Is setting a settime variable sufficient to configure timeouts?

evaluate string to modify data model in ruby

I seem to be running into a problem with Rails 3 and I can't seem to figure it out.
Here's what I am trying to do:
att1 = "column"
att2 = "1"
final_column = "#{att1}_#{att2}"
obj.final_column = 4
====> Error
-----> NoMethodError: undefined method `final_column=' for class....
If I do this it works though:
obj.column1=4
What can I do to my final_column to make it work?
Thanks!
You want to do this:
obj.send("#{final_column}=", 4)
If you want to respect the private/protected visibliy, use public_send instead of send.

Resources