how to parse multivalued field from URL query in Rails - ruby-on-rails

I have a URL of form http://www.example.com?foo=one&foo=two
I want to get an array of values ['one', 'two'] for foo, but params[:foo] only returns the first value.
I know that if I used foo[] instead of foo in the URL, then params[:foo] would give me the desired array.
However, I want to avoid changing the structure of the URL if possible, since its form is provided as a spec to a client application. is there a good way to get all the values without changing the parameter name?

You can use the default Ruby CGI module to parse the query string in a Rails controller like so:
params = CGI.parse(request.query_string)
This will give you what you want, but note that you won't get any of Rails other extensions to query string parsing, such as using HashWithIndifferentAccess, so you will have to us String rather than Symbol keys.
Also, I don't believe you can set params like that with a single line and overwrite the default rails params contents. Depending on how widespread you want this change, you may need to monkey patch or hack the internals a little bit. However the expeditious thing if you wanted a global change would be to put this in a before filter in application.rb and use a new instance var like #raw_params

I like the CGI.parse(request.query_string) solution mentioned in another answer. You could do this to merge the custom parsed query string into params:
params.merge!(CGI.parse(request.query_string).symbolize_keys)

Related

URL Structure for Comparing Items

What is the best way to structure a route for comparing multiple items?
Here's the URL example: https://versus.com/en/microsoft-teams-vs-slack-vs-somalia
How to achieve this in routes.rb file? Cannot really find anything in Internet regarding ruby gems. The only thing I can think about is url with optional params, however what if the number of params is unlimited?
you're going to have to parse the a-vs-b-vs-c yourself.
So in routes.rb, you'll have something like:
get 'compare/:compare_string', to 'compare#show'
then you'll get a parameter compare_string that you'll have to parse:
#in compare_controller.rb
def show
compare_items = params[:compare_string].split('-vs-')
# generate the comparison from the compare_items array
end
First - you probably shouldn't allow unlimited #'s of parameters in practice. Even something like 100 might break your page and/or cause performance issues and open you up to DOS attacks. I'd choose some kind of sensible/practical limit and document/enforce it (like 10, 12 or whatever makes sense for your application). At around 2k characters you'll start running into URL-length issues.
Next - is there any flexibility in the URL? Names tend to change so if you want URL's to work over time you'll need to slug-ify each of them (with something like friendly-id) so you can track changes over time. For example - could you use an immutable/unique ID AND human-readable names?
In any case, Rails provides a very flexible system for URL routing. You can read more about the various options / configurations with their Rails routing documentation.
By default a Dynamic Segment supports text like in your example, so (depending on your controller name) you can do something like:
get 'en/:items', to: 'items#compare'
If it's helpful you can add a custom constraint regexp to guarantee that the parameter looks like what you expect (e.g. word-with-dashes-vs-another-vs-something-else)
get 'en/:items', to: 'items#compare', constraints: { items: /(?:(?:[A-Z-]+)vs)+(?:[A-Z-]+)/ }
Then, in your controller, you can parse out the separate strings however you want. Something like...
def compare
items = params[:items].split('-vs-')
end

Ruby on Rails - using a block parameter as a method call

I'm having trouble with a little Ruby on Rails I'm building and need some help.
I have a Table with 20+ Columns and a corresponding XML File which can be parsed as some sort of hash with a gem. Every key would be mapped to a column and every value would be a data record in said column.
The way I access a specific value in the already parsed XML file is:
filename["crs","inputkeyhere"]
which returns the value, for example "52" or whatever.
What I am trying to do is upload the file, parse it with the gem and give each column the corresponding value.
My table (or model) is called "Attributeset" and I already know how I can access every column:
#attributeset = Attributeset.new
#attributeset.attributes.keys
So my thought process was:
Iterate over all the keys
Pass every key into a block called |a|
Use the rails possibilty to set attributes by calling the corresponding #attributeset.
Set colum attribute to the corresponding xml key
So my code would go something like this:
#attributeset.attributes.keys.each do |a|
#attributeset.a=filename["crs",a]
end
But my problem is, that ruby thinks ".a" is a method and apparently does not evaluate "a" to the block parameter.
I've read through lambdas and procs and whatnot but didn't really understand how they could work for my specific situation.
Coming from bash scripting maybe my thinking might be wrong but I thought that the .a might get evaluated.
I know I can run the block with yield, but this only works in methods as far as I know..
Any help is appreciated.
Thanks and stay healthy,
Alex
Thanks for the input!
I wanted to make it as clean as possible, and not using any temporary hashes to pass arguments.
I've found the method
write_attribute
which can be used like this:
#attributeset.write_attribute(a, xmp["crs",a])
worked perfectly for me.
You can use []= method to set values dynamically:
#attributeset.attribute_names.each do |attribute|
#attributeset[attribute] = filename["crs", attribute]
end

In Rails 5, is there a way to modify the underlying params in a controller? Or give it a default?

In a Rails 5 controller, you can call params and it returns a hash of the parameters from the request.
But you can't modify the params that way. Because what you're modifying is a copy of the params hash values, not a reference to the underlying params.
params[:starting_value] ||= "abc" # doesn't work for my purposes
What you're supposed to do is store the values elsewhere.
#starting_value = params[:starting_value] || "abc"
But if a bunch of other places in the code expect params[:starting_value], then this solution might require some messy changes.
Is there a way to set the default value of a param in the controller? Or am I going to have to do it the slightly messier way.
I could also accomplish what I want with a redirect, but that isn't ideal either.
I think you're looking for the merge! method. Docs Here
params = params.merge!(:starting_value, 'abc)
It returns the original params with the new one merged in or overwritten. Be aware that merge without an exclamation mark does not modify in place. You need it to keep the changes.

Get query string from extract URL if exists

I'm trying to get the value of a variable, prop, in a query string from a passed URL similar to what the gentleman in this thread did.
It works if there's a query string in the URL but if there isn't I get
CGI.parse undefined method 'split' for nil:NilClass error. I did research and the problem is because there is no query to split.
So my question is how can I test for the presence of any query string and then run it through CGI.parse to see if the prop query string is one of them? I assume I could probably do it through Regex but I was hoping there was a Rails solution.
Any help would be appreciated.
Thanx
Code:
I'm also trying to get the domain name of the URL referrer. That is why I have that variable in there.
url = request.env['HTTP_REFERER']
encoded_url = URI.encode(url.to_s)
parse = URI.parse(encoded_url)
domain = parse.host
puts domain
params = CGI.parse(parse.query)
puts params['prop'].first
UPDATE:
I got the error to go away by adding the attached code. But I'm still wondering if there's a better Rails solution. I'm still fairly new to Rails, so I want to make sure I'm doing it correctly.
if encoded_url.include? "?prop"
params = CGI.parse(parse.query)
puts params['prop'].first
end
The keys in query string in a URL can be accessed using params[] hash, even if the URL is not present inside the app itself. This is how it is convenient for rails to communicate with the outside world as well. Note that rails treats all the HTTP verbs equally when it comes to usage of params[] hash.
Usage: puts params[:your_key]

append a string to the params name

In my rails app I am stuck with a situation where I need to add a string to the params name,
i.e, I have a params array params[:attended], but I need to have this as params[:attended_61] and _61 must be appended. 61 is the ID of an active record row object. I have that value in #sys.id. Then please tell me how I can convert params[:attended] to params[:attended_61]. Thank you.
Billy Chan is right, what you're trying to do sounds very strange and you should probably rethink your whole approach.
But if you must do such an odd thing, you could just do this:
params[:"attended_#{#sys.id}"] = params.delete(:attended)
Or, since params will be a HashWithIndifferentAccess, you could skip the symbolification:
params["attended_#{#sys.id}"] = params.delete(:attended)
# or even
params["attended_#{#sys.id}"] = params.delete('attended')
params is simply a method that returns a Hash, you can change that Hash as needed.
You can do that as given below.
params["attended_#{#sys.id}"]
Perfectly working !!
also you can add : to the params name to be like the natural params[:var] name and it will work well also to be like that
params[:"attended_#{#sys.id}"]

Resources