Is there Arel.sql(for an array) - ruby-on-rails

I've just been bit by the warning
... 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()...
Cool. I'd already checked the column choices against the column names, so I'm in good shape. But they are a list of values, not just a single string. So I've added
['some', 'column', 'names'].map{ |string| Arel.sql(string) }
three times in the same method (for different groups of things). So I'm wondering if there is a shorthand like:
Arel.sql_elements(['some', 'list', 'of', 'strings'])
Does it exist and I'm not finding it - or am I going to monkey patch it in?

Related

how to use dynamic variable for symbols in ruby where statements

I dont how to accomplish this problem.
I faced with this problem 3 times and each time I put it in my todo list but even tho I tried to find a solution I couldnt.
For examples,
I m trying to create a query with dynamic variables of this example;
User.search(first_name_start: 'K')
there are 3 arguments in this example;
1)first_name - My model attribute
2)start - Query type (start/end/cont )
3)'3' - value
I was able to create dynamic ActiveRecord using static symbols but how am I suppose to make dynamic input
Thanks in advance
EDIT: ADDITIONAL INFORMATION
let me show you a some kind of pseudo-code
varArray.each_with_index |x,index|
queryString=varArray[i]+"_"+filterArray=[i] #lets say varArray[i], this will be first_name, an actual model attribute/a column in my db
#and filterArray like /start/end/with a filter type
#and finally valArray a string value like 'geo' or 'paul'
User.where(queryString valArray[i]).result
I tried to use send(variable) but that didnt help me either, so i dont how should i proceed,
This is one of a few cases where new fancy Ruby 1.9 syntax for defining hashes doesn't cut it. You have to use the traditional hashrocket (=>) that allows you to specify not only symbols, but any arbitrary values as hash keys:
column = "#{first_name}_size_#{query_type}".to_sym
User.where( column => value )
AFAIK, ActiveRecord is able to accept strings instead of symbols as column names, so you don't even need to call to_sym.

How default parameters are interpreted when default values are empty hashes

I was reading this SO question: https://stackoverflow.com/a/4402761/2379703 and the last post showing the rails render impl was interesting. It's signature is:
def render(options = {}, locals = {}, &block)
If the first argument is a plain string, it assigns that to options and the rest is interpreted as a hash and assigned to locals. For example:
render('partial_name', key: 'value', key2:, 'value2')
Which results in:
options = "partial_name"
locals = {key: 'value', key2:, 'value2'}
If you just pass key/value pairs, it assumes you passed a single argument of a hash and assigns it all to options and leaves locals empty:
render(partial: 'partial_name', key: 'value', key2", 'value2')
Which results in:
options = {partial: 'partial_name', key: 'value', key2:, 'value2'}
locals = {}
So my question really comes down to is: What is the logic that ruby uses to figure out which parameter gets assigned what when there are multiple optional parameters? Furthermore, it seems that hashes make the answer to this question more interesting since hashes clearly don't need to be delimited with outer {} when passed in as arguments.
A secondary observation, in a test where I used the same signature for a test method like render, and I passed the following in:
render(key: 'value', key2: 'value2', 'string')
And that results in a syntax error:
test_hash_param.rb:15: syntax error, unexpected ')', expecting =>
Why is this? Why doesn't it assign the two key/value pairs to options and sets locals to 'string'?
However this works as I assumed it would:
render({key: 'value', key2: 'value2'}, 'string')
Firstly, you can only pass hash without {} brackets as a last argument to the method. Otherwise it would be much harder for interpreter to find out where does one param ends and another starts.
Having that said, when ruby sees a list of hash-like arguments at the end of argument list, it will always treat it as a single hash. Hence in your example only options have assigned value, as locals has not been passed and default value has been used. There are a lot of issue you can find here here on stackoverflow being result of that. If you need to pass two separate hashes, you need to wrap at least first of them in brackets (and naturally the second one as well if it is not the last argument)
because you're passing hashes without boundaries, ruby has to make decisions about how to interpret them. instead of picking an arbitrary place to divide your key:value pairs into two hashes, it will just group them all in one hash. that means:
render( foo: "bar", hello: "world", "bananas")
gets read as one hash, because it can't tell where you want to end the hash. 'bananas' gets included as a key(because strings can be keys) and pops a syntax error because you didn't assign it a value.
options and locals don't have to be hashes though, because ruby variable types are dynamic. their default value is an empty hash, but if you pass two strings, they'll both be assigned strings. when you pass one string, that gets assigned to options because it is a complete variable. when you pass a symbol/string and a hash rocket though(or a symbol with the colon flipped), you're telling ruby "this is the start of a hash" so it starts looking for key:value pairs. in order to end that hash before the end of your arguments so that you can pass another argument, you have to explicitly tell ruby to stop looking for key:value pairs.

Rails validation scope of serialized text array

I have a Rails model that has a field array_field, which is a serialized text array. I want the combination of this array value and the value of another_field to be unique.
Should be straightforward, no?
class Foo < ActiveRecord::Base
validates_uniqueness_of :array_field, scope: [:another_field]
serialize :filters, Array
end
This doesn't work. However, if I switch them around in the validations,
validates_uniqueness_of :another_field, scope: [:array_field] works as expected.
Can someone explain why this is the case? Is this expected behavior?
The Postgres error for the former setup when array_field's value is nil or [] is this:
PG::SyntaxError: ERROR: syntax error at or near ")"
LINE 1: ...other_field" = 103 AND "foo"."array_field" = ) LIMIT 1
When array_field is [[1, 2], [3, 4, 5]] (a sample multiarray I was using), it's:
PG::UndefinedFunction: ERROR: operator does not exist: text = integer
LINE 1: ...other_field" = 103 AND "foo"."array_field" = 1, 2, 3, 4, 5) LIMIT 1
It seems that Rails doesn't know how to translate the serialized object for this query. Am I missing something or is this a bug?
Edit: This is occurring in Rails 4.0.2.
Second Edit:
Clarification: I understand why this is happening (Rails has custom logic for list queries), and I'm using both a custom validator to manually perform the serialization before validating and a custom serializer to avoid problems with comparisons of Yaml strings (as detailed in my other question here).
At this point I'm mostly just wondering why validates_uniqueness_of treats the primary field differently from the scope fields, and am hoping someone can shed some light.
I can't explain why the validations work one way around, but not the other.
But I think basically your problems are due to the fact that serialize only defines that an attribute is to be serialized using Yaml on save and deserialized upon load.
In other words: the only thing you say by doing serialize :filters, Array is that
when saving a Foo, serialize it's filters attribute using Yaml first,
when loading a Foo from the DB, make sure that the value of the
filters attribute is an Array after deserialization, otherwise raise an exception
It does not affect how queries are constructed. Instead, Rails' usual rules for queries are used. So an array is converted into a comma separated list of numbers. This makes sense for example when constructing a LIKE query. This is the reason why the query fails. The DB field is a string but you're trying to compare it to a list.
I haven't used native PostgreSQL array columns with Rails 4, but my guess is that these issues would solved if you used those instead a serialization-type solution. You get the added benefit of being able to search within the contents of arrays on the DB level.

REST url for resource collection, which filters resource by attribute not equal to a given value

How to design REST url for resource collection, which filters resource by attribute not equal to a given value?
For example, to get the students in 8th grade, we use
GET /students?grade=8
How to do the same, if we need to get the students not in 8th grade? And how to design for less than (<) , greater than (>) etc ?
What I am thinking of doing is including the operator as part of the argument, delimited from the value. I would also define non-symbolic operators to avoid the need for ugly URL-encoding or confusion with the equals sign after the parameter name. For your example, this would be something like this for students not in grade 8:
GET /students?grade=neq|8
Students in grades > 8 would be:
GET /students?grade=gt|8
Students between grades 8 and 10 (inclusive):
GET /students?grade=gte|8,lte|10
This approach can be extended to other filterable fields without the need to add additional parameters to modify the way each field is filtered.
Stripe has one of the most respected APIs.
They use a separate parameter for each operator separated with a dot.
For example, to search on created date:
/charges?created.gt=
/charges?created.gte=
/charges?created.lt=
/charges?created.lte=
In your case you could do something like:
/students?grade.gt=8&grade.lt=8
Or even add another operator for not:
/students?grade.not=8
One option would be to add an additional query parameter such as gradeOperator in which you could pass the operator to be used when comparing the value against the grade parameter. E.g.,
GET /students?grade=8&gradeOperator=!%3D
!%3D is the URL-encoded form of !=, so your REST API would de-encode the operator and interpret this as grade != 8.
Another approach would be to pass the value and operator in the HTTP request body. Something like this would potentially work (with the body provided in JSON as an example):
GET /students
Content-Type: application/json
{ "grade": {"value": 8, "operator": "!=" } }
That could be nice since you wouldn't have to repeat the word 'grade' in gradeOperator, the operator is simply nested inside a JSON object as the value of grade.
In either solution, you could potentially define any number of operators, including <, >, >=, <=, etc. Just be sure to properly sanitize any input operators your API receives, especially if used in a DB query, to avoid things like SQL injection attacks.

jRuby / Rails sort hash by another hash value

I have a hash that will render my html differently based on a particular variable. The variable is within the hash. So I am wondering how I can pass a hash value to a group by. to sort the rest heres what I am trying, maybe this will explain it better than me wording it.
<% grouped = svcs.group_by { |svc| svc[val[:sorttype]] } %>
val is a multidimensional hash. the first 2 key value pairs sorttype and one other are simple key and value, the 3rd piece (svcs) contains the equivilent of a 2D hash. Which if I manually type the type of sort I want to apply to it for the group by it works ie:
<% grouped = svcs.group_by { |svc| svc[:service_name] } %>
in PHP i know in a similar instance I can pass a variable of some sort to something like this and have it work. I assume such is the case here. However Im not sure how to put the variable in. Cause all the ways Ive tried don't work
It depends a little.
Rails' has a HashWithIndifferentAccess that will not distinguish between string and symbol keys; if you're using one of those, it should work as-is.
If it's not, it depends what the val entries are--if they're strings, convert to a symbol using to_sym, e.g., svc[val[:sorttype].to_sym].

Resources