I am trying to make a query to Webmaster Tool api using the Ruby Client.
params = {
start_date: "2015-01-14",
end_date: "2015-01-14"
}
AuthWebmastersService.query_search_analytics("http://www.ex.com/", params)
When I'm trying to make that request I get ArgumentError (unknown keywords: start_date, end_date), why is this happening?
Here is the method definition.
It doesn't work as expected, because Ruby converts your hash to keyword arguments, i.e.
query_search_analytics("...", {start_date: "2015-01-14", end_date: "2015-01-14"})
becomes:
query_search_analytics("...", start_date: "2015-01-14", end_date: "2015-01-14")
To get the expected result, you have to append an empty hash:
query_search_analytics("http://www.ex.com/", params, {})
This is a ruby issue with auto-expanding the last argument if is a hash.
Two workarounds:
1 - Use a proper object instead of a hash:
params = Google::Apis::WebmastersV3::SearchAnalyticsQueryRequest.new(
start_date: "2015-01-14",
end_date: "2015-01-14"
)
AuthWebmastersService.query_search_analytics("http://www.ex.com/", params)
2 - Append an empty hash as a param:
params = {
start_date: "2015-01-14",
end_date: "2015-01-14"
}
AuthWebmastersService.query_search_analytics("http://www.ex.com/", params, {})
This error:
ArgumentError (unknown keywords: start_date, end_date)
is telling you that you specified the wrong keyword attributes.
Your method takes:
def query_search_analytics(site_url, search_analytics_query_request_object = nil, fields: nil, quota_user: nil, user_ip: nil, options: nil, &block)
site_url as first argument
search_analytics_query_request_object - as second argument with default value nil
multiple keyword arguments (and a block)
When you call your method like you wrote:
site_url will have value "http://www.ex.com/"
search_analytics_query_request_object will be nill
and hash will be applied to keyword arguments and ruby will raise the error as there are no startDate and endDate keyword argument
What you need to do is to:
query_search_analytics("http://www.ex.com/", options: params)
Because it only takes the keyword arguments fields, quota_user, user_ip, options, and start_date, end_date are not among them.
Related
I'm running Ruby 2.3.1 x64 on Windows 10 x64.
My code:
class Credentials
attr_reader :username, :password
def initialize(username = nil, password = nil)
#username = username
#password = password
get_credentials if !#username || !#password #Gets credentials if none are specified
end
def get_credentials
#username = ask("Username: ") { |q| q.echo = true }
#password = ask("Password: ") { |q| q.echo = "*" }
end
end
Ignore the get_credentials wackyness, it's a gem called Highline that I'm using to hide input for security reasons.
When I do the following:
$user = Credentials.new(username: "foo", password: "bar")
I get this return:
#<Credentials:0x000000038ecf30 #password=nil, #username={:username=>"foo", :password=>"bar"}>
Likewise, calling $user.username returns the following:
{:username=>"foo", :password=>"bar"}
when it should be returning:
"foo"
and calling $user.password returns nil.
Can someone tell me why in the name of Henry Hamilton this is happening?! I've used hashed parameters many times, and it always works just fine. Why is it stuffing every parameter setting into a single parameter?
$user = Credentials.new(username: "foo", password: "bar")
You are passing just one parameter to the initialize method, a hash. The hash for the username attribute and nil for the password attribute. Try
$user = Credentials.new("foo", "bar")
Or, if you really want keyword arguments then
def initialize(username: nil, password: nil)
When you define a method/constructor you don't pass arguments by name but by value just like any other programming language, So :
$user=Credentials.new("foo","bar")
Will do what you want.
This is the default in almost every programming language, your question should have been "How did this work", it worked because ruby is dynamically typed and the syntax key1: val1,key2: val2,... is the new hash syntax(since ruby 1.9), a hash is a key-value data structure , so your :
$user=Credentials.new(username: 'foo',password: 'bar')
Is actually calling the constructor with one argument only which is username with the value {username: 'foo',password: 'bar'} and because initialize is defined with default arguments , password got a value of nil.
Now if you do want to pass arguments by name, you have to define the constructor like so :
def initialize(username: nil,password: nil)
//code
end
After that you can do :
$user=Credentials.new(username: 'foo',password: 'bar')
And expect it to behave like you want.
Notice that keyword arguments(that is passing arguments by name) are introduced in ruby 2, also notice that you can achieve the same with a constructor that accepts one parameter which is a hash like this :
def initialize(params={})
//code
end
But this way doesn't limit the number of arguments nor their names(you can call Credentials.new(fooprop: 'foovalue') and no error will be thrown), also it needs some change in code.
The Keyword arguments feature is found in some programming languages and it's useful when the function have many parameters or to make it clear for the programmer what is the parameter for.
def initialize(params={})
#username = params[:username]
#password = params[:password]
#username || #password || get_credentials #simply
end
And then:
$user = Credentials.new(username: "foo", password: "bar")
How can I turn:
Person.all.pluck(:id, :name)
to
[{id: 1, name: 'joe'}, {id: 2, name: 'martin'}]
without having to .map every value (since when I add or remove from the .pluck I have to do he same with the .map)
You can map the result:
Person.all.pluck(:id, :name).map { |id, name| {id: id, name: name}}
As mentioned by #alebian:
This is more efficient than
Person.all.as_json(only: [:id, :name])
Reasons:
pluck only returns the used columns (:id, :name) whereas the other solution returns all columns. Depending on the width of the table (number of columns) this makes quite a difference
The pluck solution does not instantiate Person objects, does not need to assign attributes to the models and so on. Instead it just returns an array with one integer and one string.
as_json again has more overhead than the simple map as it is a generic implementation to convert a model to a hash
You could simply do this
Person.select(:id,:name).as_json
You could try this as well
Person.all.as_json(only: [:id, :name])
I see three options:
1) pluck plus map:
Person.pluck(:id, :name).map { |p| { id: p[0], name: p[1] } }
2) pluck plus map plus zip and a variable to make it DRY-er:
attrs = %w(id name)
Person.pluck(*attrs).map { |p| attrs.zip(p).to_h }
3) or you might not use pluck at all although this is much less performant:
Person.all.map { |p| p.slice(:id, :name) }
If you use postgresql, you can use json_build_object function in pluck method:
https://www.postgresql.org/docs/9.5/functions-json.html
That way, you can let db create hashes.
Person.pluck("json_build_object('id', id, 'name', name)")
#=> [{id: 1, name: 'joe'}, {id: 2, name: 'martin'}]
Could go for a hash after the pluck with the ID being the key and the Name being the value:
Person.all.pluck(:id, :name).to_h
{ 1 => 'joe', 2 => 'martin' }
Not sure if this fits your needs, but presenting as an option.
You can use the aptly-named pluck_to_hash gem for this:
https://github.com/girishso/pluck_to_hash
It will extend AR with pluck_to_hash method that works like this:
Post.limit(2).pluck_to_hash(:id, :title)
#
# [{:id=>213, :title=>"foo"}, {:id=>214, :title=>"bar"}]
#
Post.limit(2).pluck_to_hash(:id)
#
# [{:id=>213}, {:id=>214}]
It claims to be several times faster than using AR select and as_json
There is pluck_all gem that do almost the same thing as pluck_to_hash do. And it claims that it's 30% faster. (see the benchmark here).
Usage:
Person.pluck_all(:id, :name)
If you have multiple attributes, you may do this for cleanliness:
Item.pluck(:id, :name, :description, :cost, :images).map do |item|
{
id: item[0],
name: item[1],
description: item[2],
cost: item[3],
images: item[4]
}
end
The easiest way is to use the pluck method combined with the zip method.
attrs_array = %w(id name)
Person.all.pluck(attrs_array).map { |ele| attrs_array.zip(ele).to_h }
You can also create a helper method if you are using this method through out your application.
def pluck_to_hash(object, *attrs)
object.pluck(*attrs).map { |ele| attrs.zip(ele).to_h }
end
Consider modifying by declaring self as the default receiver rather than passing Person.all as the object variable.
Read more about zip.
Here is a method that has worked well for me:
def pluck_to_hash(enumerable, *field_names)
enumerable.pluck(*field_names).map do |field_values|
field_names.zip(field_values).each_with_object({}) do |(key, value), result_hash|
result_hash[key] = value
end
end
end
I know it's an old thread but in case someone is looking for simpler version of this
Hash[Person.all(:id, :name)]
Tested in Rails 5.
I have a JSON string which has been generated by Jbuilder:
json = "{name: 'Peter', email: 'peter#stackoverflow.com'}"
This is currently a string. However I want to combine it into a new hash (ideally in Ruby) before finally outputting it as JSON.
i.e.
output = {result: :success, data: json}
However if I convert this to JSON the json value gets double-encoded such that it's sent as a string:
output.to_json
#=> "{\"result\":\"success\",\"data\":\"{name: 'Peter', email: 'peter#stackoverflow.com'}\"}"
Now I could parse the JSON into a Ruby hash and then re-output it but that seems like a big fat waste of parsing when what I'd really like to do is to say "hey, this node is already JSON, don't re-encode it already!".
Is there any equivalent to the raw() method Rails has in views? i.e.
output = {result: :success, data: raw(json)}
so that the json evaluation of this then becomes:
output.to_json
#=> "{\"result\":\"success\",\"data\": {\"name\":\"Peter\",\"email\":\"peter#stackoverflow.com\"}"
Here’s a way you can do this, it’s a bit of a hack but you might find it useful.
First restating the problem:
# Note the quotes, your example isn't actually valid
json = "{\"name\": \"Peter\", \"email\": \"peter#stackoverflow.com\"}"
output = {result: :success, data: json}
# Without changing anything
puts JSON.generate(output)
This results in the following, where the value of data is a single string:
{"result":"success","data":"{\"name\": \"Peter\", \"email\": \"peter#stackoverflow.com\"}"}
The json gem uses a to_json method that is added to all objects to convert them to json, so the simplest fix would be to replace that method on objects you want to behave differently:
# As before
json = "{\"name\": \"Peter\", \"email\": \"peter#stackoverflow.com\"}"
# Replace to_json on the singleton object
def json.to_json *args
self
end
output = {result: :success, data: json}
# Generate the output (output.to_json gives the same result)
puts JSON.generate(output)
This creates the following, where the data value is now itself a hash, as desired:
{"result":"success","data":{"name": "Peter", "email": "peter#stackoverflow.com"}}
A cleaner way to do this, to avoid manipulating singletons in your code could be to create a subclass of string that has this behaviour:
class JsonSafeString < String
def to_json *args
self
end
end
You can now create a JsonSafeString when you want the contents included directly in a JSON object:
json = "{\"name\": \"Peter\", \"email\": \"peter#stackoverflow.com\"}"
output = {result: :success, data: JsonSafeString.new(json)}
puts JSON.generate(output)
The result is the same as above:
{"result":"success","data":{"name": "Peter", "email": "peter#stackoverflow.com"}}
You could wrap the call to JsonSafeString.new in a method like raw_json if you wanted.
Obviously this leaves the task of ensuring your string is valid to you – the main point of using a library for this is the user doesn’t have to concern themselves with things like whether to use single or double quotes, so you could be vulnerable to generating invalid JSON if you’re not careful. Also this is just a quick hack, there are probably a load of things I haven’t considered. In particular I haven’t taken character encodings into account, so watch out.
This doesn't address your question, but may help you avoid it altogether...
Do you really need to generate your json variable into JSON before adding it to the hash? Jbuilder can generate a hash just as easily as a JSON string, e.g.:
hash = Jbuilder.new do |json|
json.name 'Peter'
json.email 'peter#stackoverflow.com'
end.attributes!
# => {"name"=>"Peter", "email"=>"peter#stackoverflow.com"}
output = {result: :success, data: hash}
eval will put it out as raw code.
eval "{name: 'Peter', email: 'peter#stackoverflow.com'}"
=> {:name=>"Peter", :email=>"peter#stackoverflow.com"}
And the results.
output = {result: :success, data: eval("{name: 'Peter', email: 'peter#stackoverflow.com'}") }
=> {:result=>:success, :data=>{:name=>"Peter", :email=>"peter#stackoverflow.com"}}
And to string
output.to_s
=> "{:result=>:success, :data=>{:name=>\"Peter\", :email=>\"peter#stackoverflow.com\"}}"
And JSON
require 'json'
=> true
output.to_json
=> "{\"result\":\"success\",\"data\":{\"name\":\"Peter\",\"email\":\"peter#stackoverflow.com\"}}"
Here is a sample code from a RSpec code:
describe Thing do
def create_thing(options)
thing = Thing.new
thing.set_status(options[:status])
thing
end
it "should do something when ok" do
thing = create_thing(:status => 'ok')
thing.do_fancy_stuff(1, true, :move => 'left', :obstacles => nil)
...
end
end
So my confusion is mostly on this line:
thing.set_status(options[:status])
So create_thing method has an "option" parameter then we are passing status part of that parameter? Can someone explain this syntax in some easier words?
options is just a variable. The part you need to understand is this part
thing = create_thing(:status => 'ok')
You are basically passing a Hash to create_thing and therefore options is a hash. Then you can access the value of the status key by doing options[:status].
If the above mentioned line looked like this
thing = create_thing("Foo")
options would be "Foo" and you could get an error trying to do something like options[:status]
create_thing takes an argument called options.
Options is expected to be a hash (most likely).
You're passing the hash value with the key (a symbol):option to the set_status method.
You've passed an implicit hash to create_thing:
create_thing({ status: 'ok' }) is the same as
create_thing(status: 'ok') is the same as
create_thing(:status => 'ok')
Any way you call it, you access that value via options[:status].
In Ruby 1.8, using the URI standard library, I can parse
http://au.easyroommate.com/content/common/listing_detail.aspx?code=H123456789012&from=L123456789012345
using URI.split to get
["http", nil, "au.easyroommate.com", nil, nil,
"/content/common/listing_detail.aspx", nil,
"code=H123456789012&from=L123456789012345", nil]
But is it possible to get the H123456789012 bit from the query portion without using my own hackery (eg splitting by & and then getting the bit that matches /code.(.*)/ ?
You're looking for the CGI::parse method
params = CGI::parse("query_string")
# {"name1" => ["value1", "value2", ...],
# "name2" => ["value1", "value2", ...], ... }
You could use Rack::Utils which has a method called parse_nested_query which you could pass in the query string from the URL:
Rack::Utils.parse_nested_query(uri.query_string)
This will then return a Hash object representing the query string allowing you to gain access to the code.