Looping ruby hash and output specific element - ruby

I am trying to loop through a hash for specific data to output. If I want to output all usernames. This is how I can do it one at a time but its not what I want.
puts username = json["users"][0]["username"]
puts username = json["users"][1]["username"]
also tried
json.each { |x| puts json["users"][x]["username"]}
This is the hash structure
{"success"=>true, "users"=>[{"id"=>"1523493", "username"=>"myname","age"=>"21"},{"id"=>"653172", "username"=>"anothername","age"=>"65"}]}
sorry I didnt make my question clear enough. I am wanting to iterate the hash for "username" and then i can loop through each username and output specific data before moving to next username

You can loop your json like this
json["users"].each do |u|
username = u["username"]
#Do some logic with username
#like user = User.find_by_username(username)
end

You can get all the usernames in one go by doing something like this:
json = { "users" => [{"id"=>"1523493", "username"=>"myname"},{"id"=>"653172", "username"=>"anothername"}] }
json["users"].map { |user| user["username"] }
# => ["username", "anothername"]
The above will provide you with an array of usernames to do with as you see fit. :)
Hope it helps!

h = {"success"=>true,
"users"=>[{"id"=>"1523493", "username"=>"myname","age"=>"21"},
{"id"=>"653172", "username"=>"anothername","age"=>"65"}]}
h["users"].map { |user| user["username"] }
#=> ["myname", "anothername"]

Related

Ruby exclude specific data from array of hashes

I've got response which hash and array of hashes:
"id"=>67547,
"description"=>"project",
"actors"=>
[
{"id"=>123,
"displayName"=>"John Doe",
"type"=>"atlassian-user-role-actor",
"name"=>"john.doe",
"actorUser"=>{"accountId"=>"some_id"}},
{"id"=>456,
"displayName"=>"Chris Sth",
"type"=>"atlassian-user-role-actor",
"name"=>"chris.sth",
"actorUser"=>{"accountId"=>"some_id"}},
{"id"=>789,
"displayName"=>"Testing Name",
"type"=>"atlassian-user-role-actor",
"name"=>"testing.name",
"actorUser"=>{"accountId"=>"some_id"}},
]
What I need is to pull the name for each hash['actors'] and convert it to the email address. The thing is I need to skip names which are defined as EXCLUDED_NAMES
EXCLUDED_NAMES = %w[
chris.sth
removed1258986304
john.doe
other.names
].freeze
private_constant :DEFAULT_EXCLUDED_NAMES
I was trying to something like below but still get all names:
def setup_email
dev_role['actors'].map do |user|
if user.include?(EXCLUDED_NAMES)
user.delete
else
"#{user['name']}#example.com"
end
end
end
You can get an array of valid emails with:
emails = dev_role['actors'].map do |user|
"#{user['name']}#example.com" unless EXCLUDED_NAMES.include?(user['name'])
end
Array will only contain 'testing.name#example.com'
If dev_role['actors'] is this:
[
{"id"=>123,
"displayName"=>"John Doe",
"type"=>"atlassian-user-role-actor",
"name"=>"john.doe",
"actorUser"=>{"accountId"=>"some_id"}},
{"id"=>456,
"displayName"=>"Chris Sth",
"type"=>"atlassian-user-role-actor",
"name"=>"chris.sth",
"actorUser"=>{"accountId"=>"some_id"}},
{"id"=>789,
"displayName"=>"Testing Name",
"type"=>"atlassian-user-role-actor",
"name"=>"testing.name",
"actorUser"=>{"accountId"=>"some_id"}},
]
then it is certain that user in each block would be a Hash object:
{
"id"=>123,
"displayName"=>"John Doe",
"type"=>"atlassian-user-role-actor",
"name"=>"john.doe",
"actorUser"=>{"accountId"=>"some_id"}
}
So, doing user["name"], should produce: "john.doe".
Now, that we have an exclusion list EXCLUDED_NAMES we could use include? like so on it:
EXCLUDED_NAMES.include?(user["name"])
=> # true if the name is in the EXCLUDED_NAMES
So, all you need is a small change in your code to fix the condition:
def setup_email
dev_role['actors'].map do |user|
if EXCLUDED_NAMES.include?(user["name"])
user.delete
else
"#{user['name']}#example.com"
end
end
end
There is one problem though, the user.delete would not work as it expects an argument that is supposed to be a key to the hash object.
This can be fixed through by using reject or select(changing to reject as it reads better):
def setup_email
dev_role['actors'].reject do |user|
EXCLUDED_NAMES.include?(user["name"])
end.map{ |user| user["name"] }
end
The nature of the method seems to be returning an array/list, so I would insist that the name of such methods should be plural: setup_emails.
I'd create a lookup hash based upon the the actor name. Then retrieve the values that are not in EXCLUDED_NAMES.
When actors can contain duplicate names:
actors = dev_role['actors'].group_by { |actor| actor['name'] }
actors = actors.values_at(*actors.keys - EXCLUDED_NAMES).flatten(1)
When actors can't contain duplicate names:
actors = dev_role['actors'].to_h { |actor| [actor['name'], actor] }
actors = actors.values_at(*actors.keys - EXCLUDED_NAMES)
Then:
emails = actors.map { |actor| "#{actor['name']}#example.com" }
You could also solve this with an Array#reject/Array#map combination:
emails = dev_role['actors']
.reject { |actor| EXCLUDED_NAMES.include?(actor['name']) }
.map { |actor| "#{actor['name']}#example.com" }
The above might be slower when using a large EXCLUDED_NAMES array.
dev_role=dev_role.to_hash
actors=dev_role["actors"]
for each_actor in actors
if EXCLUDED_NAMES.include?(each_actor["name"])==false
p "#{each_actor['name']}#example.com"
end
end

Create a Ruby Hash out of an xml string with the 'ox' gem

I am currently trying to create a hash out of an xml documen, with the help of the ox gem
Input xml:
<?xml version="1.0"?>
<expense>
<payee>starbucks</payee>
<amount>5.75</amount>
<date>2017-06-10</date>
</expense>
with the following ruby/ox code:
doc = Ox.parse(xml)
plist = doc.root.nodes
I get the following output:
=> [#<Ox::Element:0x00007f80d985a668 #value="payee", #attributes={}, #nodes=["starbucks"]>, #<Ox::Element:0x00007f80d9839198 #value="amount", #attributes={}, #nodes=["5.75"]>, #<Ox::Element:0x00007f80d9028788 #value="date", #attributes={}, #nodes=["2017-06-10"]>]
The output I want is a hash in the format:
{'payee' => 'Starbucks',
'amount' => 5.75,
'date' => '2017-06-10'}
to save in my sqllite database. How can I transform the objects array into a hash like above.
Any help is highly appreciated.
The docs suggest you can use the following:
require 'ox'
xml = %{
<top name="sample">
<middle name="second">
<bottom name="third">Rock bottom</bottom>
</middle>
</top>
}
puts Ox.load(xml, mode: :hash)
puts Ox.load(xml, mode: :hash_no_attrs)
#{:top=>[{:name=>"sample"}, {:middle=>[{:name=>"second"}, {:bottom=>[{:name=>"third"}, "Rock bottom"]}]}]}
#{:top=>{:middle=>{:bottom=>"Rock bottom"}}}
I'm not sure that's exactly what you're looking for though.
Otherwise, it really depends on the methods available on the Ox::Element instances in the array.
From the docs, it looks like there are two handy methods here: you can use [] and text.
Therefore, I'd use reduce to coerce the array into the hash format you're looking for, using something like the following:
ox_nodes = [#<Ox::Element:0x00007f80d985a668 #value="payee", #attributes={}, #nodes=["starbucks"]>, #<Ox::Element:0x00007f80d9839198 #value="amount", #attributes={}, #nodes=["5.75"]>, #<Ox::Element:0x00007f80d9028788 #value="date", #attributes={}, #nodes=["2017-06-10"]>]
ox_nodes.reduce({}) do |hash, node|
hash[node['#value']] = node.text
hash
end
I'm not sure whether node['#value'] will work, so you might need to experiment with that - otherwise perhaps node.instance_variable_get('#value') would do it.
node.text does the following, which sounds about right:
Returns the first String in the elements nodes array or nil if there is no String node.
N.B. I prefer to tidy the reduce block a little using tap, something like the following:
ox_nodes.reduce({}) do |hash, node|
hash.tap { |h| h[node['#value']] = node.text }
end
Hope that helps - let me know how you get on!
I found the answer to the question in my last comment by myself:
def create_xml(expense)
Ox.default_options=({:with_xml => false})
doc = Ox::Document.new(:version => '1.0')
expense.each do |key, value|
e = Ox::Element.new(key)
e << value
doc << e
end
Ox.dump(doc)
end
The next question would be how can i transform the value of the amount key from a string to an integer befopre saving it to the database

Fetch under one key ruby

I have this json:
{"user"=>
{"name"=>"Lebron James",
"email"=>"lebron.james#gmial.com",
"time_zone"=>"America/Chicago",
"contact"=>
[{"id"=>"PO0JGV7",
"type"=>"email_contact_method_reference",
"summary"=>"Default",
"self"=>
"https://pagerduty.com/users/000000/contact/000000",
"html_url"=>nil},
{"id"=>"000000",
"type"=>"phone_contact_method_reference",
"summary"=>"Mobile",
"self"=>
"https://pagerduty.com/users/000000/contact/000000",
"html_url"=>nil},
{"id"=>"000000",
"type"=>"push_notification_contact_method_reference",
"summary"=>"XT1096",
"self"=>
"https://api.pagerduty.com/users/000000/contact/000000",
"html_url"=>nil},
{"id"=>"000000",
"type"=>"sms_contact_method_reference",
"summary"=>"Mobile",
"self"=>
"https://pagerduty.com/users/000000/methods/000000",
"html_url"=>nil}],
I want to be able to retrieve the values of the self keys, but only the ones that has "type" => "email_contact_method_reference" and "summary"=>"Mobile". This is what I thought would work.
phone = File.open("employee_phone_api.txt", "w+")
jdoc.fetch("user").fetch("contact_methods").each do |contact|
if contact["type"] == "email_contact_method_reference" and contact["summary"] == "Mobile"
phone.puts contact["self"]
else
end
end
Thoughts? And/or suggestions?
No need to use #each, as there are more expressive ways of handling this problem. As with many Ruby problems, you want to get an Array and then transform it. In this case, you want to select certain contacts and then pull out particular values.
Your sample hash has a "contact" key but not a "contact_methods" key. I'm using "contact" for my example. Also, your sample contains no objects that meet the criteria, so I'm modifying it to include one.
First we get an Array of all the contacts:
contacts = jdoc.fetch("user").fetch("contact")
Then we filter them to the desired type using Enumerable#select, which results in an Array of a single Hash object:
email_contacts = contacts.select { |contact| contact['type'] == 'email_contact_method_reference' && contact['summary'] == 'Mobile' }
#=> [{"id"=>"PO0JGV7", "type"=>"email_contact_method_reference", "summary"=>"Mobile", "self"=>"https://pagerduty.com/users/000000/contact/000000", "html_url"=>nil}]
Next we map out just the information we want:
urls = email_contacts.map { |contact| contact['self'] }
This results in urls being assigned an Array of a single string:
#=> ["https://pagerduty.com/users/000000/contact/000000"]
In the real world, you will want to have a method that accepts arguments, making the logic flexible. You might do something like this:
def fetch_urls(doc, type, summary)
doc.fetch("user").fetch("contact")
.select { |contact| contact['type'] == type && contact['summary'] == summary }
.map { |contact| contact['self'] }
end
>> fetch_urls(jdoc, 'email_contact_method_reference', 'Mobile')
#=> ["https://pagerduty.com/users/000000/contact/000000"]
Now that you have a working method, you can use it in your file writer:
>> phone = File.open("employee_phone_api.txt", "w+")
>> phone.puts fetch_urls(jdoc, 'email_contact_method_reference', 'Mobile').join("\n")
>> phone.close
>> File.read(phone)
#=> "https://pagerduty.com/users/000000/contact/000000\n"

How to print all linux users with Puppet and ruby?

I want to create a facter that returns all users.
Facter.add("sysusers") do
setcode do
File.readlines('/etc/passwd').each do |line|
line.match(/^[^:]+/)[0]
end
end
end
Then in my .pp file I have this:
$users = inline_template("<%= scope.lookupvar('sysusers') %>")
$users.each |String $user| {
notify { "$user":}
}
This should work but the facter returns just one letter at a time.
So notify { "$user":} just prints:
Notify[r]
Notify[o]
And then it craches because the next letter is also "o" (two o`s in "root" and root is the first user stated in /etc/passwd).
So how can I print all the users?
EDIT
With the edit to:
Facter.add("sysusers") do
setcode do
File.readlines('/etc/passwd').each do |line|
line.match(/^[^:]+/).to_s
end
end
end
Then the output is:
root#mymachine]# facter sysusers
[
"root:x:0:0:root:/root:/bin/bash
",
"bin:x:1:1:bin:/bin:/usr/bin/nologin
",
"daemon:x:2:2:daemon:/:/usr/bin/nologin
...
...
So it still does not seem to work as expeced.
This is the match you want.
line.match(/^[^:]+/).to_s
When you add [0], it is taking the first character from the string that is the user name.
EDIT
File.readlines('/etc/passwd').collect do |line|
line.match(/^[^:]+/).to_s
end
That will collect an array which to be returned in your setcode.
Parsing /etc/passwd is a clunky approach to your problem.
It's cleaner to use the Etc module
require 'etc'
result = []
Etc.passwd { |user| result << user.name }
result
Use the following ruby code, which reads and prints the user names from /etc/passwd file.
IO.readlines("/etc/passwd").each do |val|
user = val.split(":").first
puts user
end

How should I check passwords in Ruby?

I am using Ruby to manage users in a database.
I am using pass = Digest::MD5.hexdigest() to encrypt passwords before they are being added to the database.
I need to create a function to check that a given password matches that stored in the database but I'm not sure how I should do it.
Do I use pass = Digest::MD5.hexdigest() on the user provided password, and then check that against what is returned from the database?
This is correctpassword?:
def correctpassword?(nick, pass)
user = nick
pass = Digest::MD5.hexdigest(pass)
db = SQLite3::Database.new "database.db"
db.execute("SELECT * FROM t1 WHERE user = ? and pass = ?", user, pass)
!results.empty?
end
This is attempting to use correctpassword?:
if clt.registered?(#nick)
if clt.correctpassword?(#nick, #pass)
sv_send 'NOTICE', #nick, ":Correct password."
else
sv_send 'NOTICE', #nick, ":Incorrect password."
end
end
I don't see either notices. Using correctpassword? seems to break things.
This works though:
if clt.registered?(#nick)
sv_send 'NOTICE', #nick, ":This account is registered."
end
There is no assignment for results var. Do results = db.execute("SELECT * FROM t1 WHERE user = ? and pass = ?", user, pass)
MD5 is a one-way hash encryption algorithm. There is no way to directly decrypt a MD5 hash. The algorithm itself uses modular arithmetic to package the serialized string and there is no way to go backwards from that.
So I think you should convert the password to MD5 which user try to enter and compare that hash with stored encrypted password in db.
Eg:
> stored_password_in_db = Digest::MD5.hexdigest('gagan')
#=> "cc18a19beff0bdf874861a4dae6124b6"
> user_enter_password_for_login = Digest::MD5.hexdigest('gagan')
#=> "cc18a19beff0bdf874861a4dae6124b6"
> stored_password_in_db == user_enter_password_for_login
#=> true
> user_enter_password_for_login = Digest::MD5.hexdigest('Gagan')
#=> "f52bb23033354697e8f55abdaed9d94f"
> stored_password_in_db == user_enter_password_for_login
#=> false

Resources