Get query string from extract URL if exists - ruby-on-rails

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]

Related

Can't modify frozen String on Hash

I am a bit confused with the frozen string and utilizing them with test cases.
I just added the following line at the top of my test cases :
# frozen_string_literal: true
And i have the following two test cases:
test "Create upload invoice invalid invoice id" do
post :upload, params = {invoices_data: [{invoice_id: 987654, unit_id: 1321}]}
assert_response :not_found
end
test "Create upload invoice request to fortnox with non array request parameter" do
request = {invoices_data: {invoice_id: "invoice.id", unit_id: 321}}
post :upload_invoices, params = request
assert_response :bad_request
end
All of a sudden my second test failed with
RuntimeError: can't modify frozen String
at this line
post :upload_invoices, params = request
however, if I change the string to some symbol for instance :invoice_id then it works just fine.
Can someone guide why about the following two things:
Why does sending a string value fails in this case reporting that I
am trying to modify a String and which string value I am trying to
modify?
Why does it fail on post request, if it has to fail then it
should fail when creating the request i.e request = {invoices_data: {invoice_id: "invoice.id", unit_id: 321}}
What i can do to send string value instead of Symbol in the hash?
1a) Sending a string value fails in this case because your upload_invoices controller action attempts to modify the invoice_id parameter itself. (Or you're running an old version of Rails where the #post method itself attempts to modify the invoice_id parameter by converting it to UTF-8 encoding.)
1b) The string value you're trying to modify is "invoice.id".
2 ) It fails on the post request and not the assignment to the request variable because the assignment to the request variable is not where the attempted modification happens. The frozen string literal is attempted to be modified by the call to #post. See answer 1a above.
3 ) You can send a non-frozen string value in the hash a few different ways. You could remove the # frozen_string_literal: true magic comment, but I feel you don't want to do that. Otherwise, the simplest thing to do is to send along a duplicate of the string with either +'invoice.id' or the less esoteric 'invoice.id'.dup. Or you can create a non-literal string with something silly like ['invoice', 'id'].join('.') or :invoice.to_s. No doubt there are other ways.
However, it seems EXTREMELY unlikely you want to pass a string here at all. The invoice_id parameter is almost assuredly an integer, and passing a string to it makes little sense unless I guess you're trying to test that the controller action can handle that kind of erroneous input. If so, one of the string duplication techniques +'string_literal'/'string_literal'.dup would be your best option.
I would wager by the name of the test that you're actually trying to send along a real invoice_id which means you don't want to pass along a string, but instead an integer. Maybe the ID of an Invoice fixture you have setup?
And on another slightly unrelated note, you're not passing params to the #post method properly. It should be params: ... not params = ....

Saving Mutiple arrays with strong parameters Ruby on Rails 4

I have an ajax response like this ,
google_searched_locations [{"geometry":{"location":{"J":31.482273,"M":74.33069999999998}},"icon":"https://maps.gstatic.com/mapfiles
/place_api/icons/restaurant-71.png","id":"b93a99a46343de01d0d928f99470f9b0f5f6f11d","name":"Dunkin' Donuts"
,"place_id":"ChIJSeoh6hkEGTkRsd0e1crAbHU","rating":4.3,"reference":"CnRhAAAAewgE30hrAcax1xdGPIf7M863
bOtNhRgfnncMx17uWMgtdTyGHGbTO76LX6yXsPyB4PcvfVzIeeIR1bxG0oSambqMYxFWwqHY3Cyfs6uWFp2QbVkGObvQ1LlTrdqLh1eZVgX8aL0iRFFhAnHEM8u1RxIQACDCn2BMD3IiG7tKri31BRoULMSagTU-EmxswgLxzCOWPVVnlpI"
,"scope":"GOOGLE","types":["restaurant","food","point_of_interest","establishment"],"vicinity":"Lahore"
,"html_attributions":[]}]
After JSON.parse(thisString) , I get the required result by using params["places"][0]["geometry"] and then looping over it , but I know that it's not a better way when Rails have strong parameter there , so I tried some solutions after searching , below is my strong parameters function
def google_places
json_params = ActionController::Parameters.new(JSON.parse(request.body.read) )
json_params.require(:google_searched_locations).permit(:icon)
end
but it gives the following error ,
JSON::ParserError in SearchesController#searchResults
757: unexpected token at 'places=%5B%7B%22geometry
Can anyone make us out of this mess , and tell us what's happening here and where is the right way to go .
Thanks in advance :)
You do not need to manually parse JSON parameters. Rails will automatically parse the parameters provided the request has the correct format (more specifically the correct mime type headers). In fact doing so will be slower and use more memory since you are doing the same parsing work twice.
To allow an array of parameters you simply use the hash key and an array of the permitted attributes for the nested params.
def google_places
params.permit(places: [:icon, { location: [:H, :L] }])
end
If possible you should change the H and L parameters to lowercase so that you can map them directly to attributes without violating the ruby conventions of lowercase attributes.

Rails: Converting URL strings to params?

I know Rails does this for you, but I have a need to do this myself for examples. Is there a simple, non-private method available that takes a string and returns the hash of params exactly as Rails does for controllers?
Using Rack::Utils.parse_nested_query(some_string) will give you better results since CGI will convert all the values to arrays.
I found a way after a more intense Google search.
To convert url string to params:
hash = CGI::parse(some_string)
And (as bonus) from hash back to url string:
some_string = hash.to_query
Thanks to: https://www.ruby-forum.com/topic/69428
In model you can write a query like
def to_param
"-#{self.first_name}" +"-"+ "#{self.last_name}"
end
More info
http://apidock.com/rails/ActiveRecord/Base/to_param
It will generate a url like http://ul.com/12-Ravin-Drope
More firendly url you can consult
https://gist.github.com/cdmwebs/1209732

How to refer to the current record in a Rails query?

In a Rails controller I'm using a JSON request to an external database to return integers that are used to order a collection of records:
Model.order(JSON.parse(open("http://myapp.com/models/#{:id}")).read)['attribute'])
I want to make this dynamic to aid switching between environments. Something like:
Model.order(JSON.parse(open(model_url(model))).read)['attribute'])
This is not correct, and model_url(model) is returning an error:
undefined local variable or method 'model'
How do I refer to self in query?
There must be a more elegant solution than
...JSON.parse(open("#{root_url}/models/{:id}"))....
EDIT:
Lightswitch05's answer below does anser the question I asked. The query should reference params[:id] to get the url of the current record.
In fact, I have decided to move this JSON call into a virtual attribute on the model. This means I can simply call Model.order(:my_virtual_attribute). While this solution brings its own share of issues—I needed to make url_helpers available to the model—in the long run I think this will be a cleaner solution.
As per our discussion the problem is that model is not defined. Since all you are trying to do with model is get the url to it, all you really need is params[:id]. This will fix your error message:
Model.order(JSON.parse(open(model_url(params[:id]))).read)['attribute'])
where model has been replaced with params[:id]
Change your code:
Model.order(JSON.parse(open(model_url(model))).read)['attribute'])
to:
model_tableized = Model.to_s.tableize
model_url = "#{model_tableized}_url(#{model_tableized.chomp('s')})"
Model.order(JSON.parse(open(model_url).read)["attribute"])
I think that should work.
You need the name method.
If you're using the model, and not an instance of the model, it would be Model.name
If you're using an instance, it would be
#model = Model.first
#model.class.name

how to parse multivalued field from URL query in 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)

Resources