Adding a new field to each array element in .lua - lua

Please tell me, if there are any errors here and how to make the code more clean.
There is a space "company". Inside it's a string "type" and "information" map. Inside this map is a "job" object and an array of "users" objects. The "users" array consists of 2 objects. Each object has 4 fields.
I need to add a new field:
status = "UPDATED"
inside each object in the "users" array, under a certain condition
"company": {
"type" : "01",
"information":
{
"job":
{
"job_name": "N",
"job_address": 1670392687114,
"job_salary": 1234567890123,
"contacts": 0
},
"users":
[
{
"id": 1,
"name": "Alex",
"rate": 4,
"address": "bla bla bla"
},
{
"id": 2,
"name": "Jenifer",
"rate": 5,
"address": "bla bla bla"
}
]
}
}
My logic is next:
if tuple.type == "01" or tuple.type == "02" or tuple.type == "03" or tuple.type == "04" then
for _, attr in pairs(users) do
attr.status = "UPDATED"
end
end
Is it correct to add a new status="UPDATED" field to each object in the array "users" here?
Does this entry exist?
And yet, tell me please, can I somehow make the condition in if more beautiful? For example analog in Java list.contains()
Updated:
The final version after adding new field "status" should look like this (see image)

Is it correct to add a new status="UPDATED" field to each object in
the array "users" here? Does this entry exist?
If you want the table to have a status field with value "UPDATED" then attr.status = "UPDATED"is correct.
This only makes sense if you'll add more non-updated users to that table later though as you're updating the entire list. you could as well just mark the users table object as updated.
And yet, tell me please, can I somehow make the condition in if more
beautiful? For example analog in Java list.contains()
No but you could write your own function.
function table.contains(tbl, value)
for _, v in pairs(tbl) do
if (v == value) then return true end
end
return false
end
if table.contains({"01", "02", "03", "04"}, tuple.type) then
for _, attr in pairs(users) do
attr.status = "UPDATED"
end
end
Alternatively you could use a lookup table
local isUpdateType = {
["01"] = true,
["02"] = true,
["03"] = true,
["04"] = true,
}
and later
if isUpdateType[tuple.type] then end
How to solve this depends very much on what you consider "beautiful" here.

Related

How to rename fields in array in .lua

I'm new in .lua. I read the documentation, but didn't find the answer to my question.
There is a space "company".
Inside it's an "information" map.
Inside this map is a "job" object and an array of "users" objects.
The "users" array consists of 2 objects. Each object has 4 fields.
I need to rename 2 fields:
Old field names -> rate and address.
New field names -> user_rate and user_address
"company": {
"information":
{
"job":
{
"job_name": "N",
"job_address": 1670392687114,
"job_salary": 1234567890123,
"contacts": 0
},
"users":
[
{
"id": 1,
"name": "Alex",
"rate": 4,
"address": "bla bla bla"
},
{
"id": 2,
"name": "Jenifer",
"rate": 5,
"address": "bla bla bla"
}
]
}
}
My solution was the following:
for _, tuple in space:pairs() do
if tuple.params ~= nil or tuple.params.offer_params ~= nil then
local information = tuple.information or {}
local users = information.users
for _, attr in pairs(users) do
local user_rate = attr.rate
local user_address = attr.address
end
local key = {}
for _, part in ipairs(key_parts) do table.insert(key, tuple[part.fieldno]) end
space:update(key, { {'=', 'information', information} })
Here I am trying to rename rate to -> user_rate and address to -> user_address and then doing an update.
Please tell me what is wrong here.
Please help me figure it out.
for _, attr in pairs(users) do
local user_rate = attr.rate
local user_address = attr.address
end
he're you're just creating two local variables and assign a value to them. After the loop they run out of scope. So you really didn't do anything useful.
If you want to "rename" table fields you need to assign the values of those fileds to the new field names and assign nil to the old field.
for _, user in pairs(users) do
user.user_rate = user.rate
user.rate = nil
user.user_address = user.address
user.address = nil
end
For more you might want to implement a function that does that to keep your code clean.

Correct test in .lua

I'm new in lua.
Sorry for too much text, but I really need help
There is a space "company". Inside it's "information" map. Inside this map is a "job" object and an array of "users" objects. The "users" array consists of 2 objects. Each object has 4 fields.
"company": {
"company_id" : 1,
"type" : "01",
"number" : "YES",
"information":
{
"job":
{
"job_name": "N",
"job_address": 1670392687114
},
"users":
[
{
"id": 1,
"name": "Alex",
"rate": 4,
"address": "bla bla bla"
},
{
"id": 2,
"name": "Jenifer",
"rate": 5,
"address": "bla bla bla"
}
]
}
}
There is a migration for renaming fields in a space and adding new field data, provided:
local space_name = 'company'
local space = box.space[space_name]
local key_parts = space.index.primary.parts
local updated = 0
local counter = 0
local isUpdateType = {
["01"] = true,
["02"] = true,
["03"] = true,
["04"] = true,
}
for _, tuple in space:pairs() do
if tuple.information ~= nil and tuple.information.users ~= nil then
local information = tuple.information
local users = tuple.information.users
-- rename fields and assign nil to the old fields
for _, attr in pairs(users) do
attr.user_rate = attr.rate
attr.rate = nil
attr.user_address = attr.address
attr.address = nil
end
-- update data according to the condition
if isUpdateType[tuple.type] and tuple.number == "YES" then
for _, attr in pairs(users) do
attr.status = "UPDATED"
end
end
local key = {}
for _, part in ipairs(key_parts) do table.insert(key, tuple[part.fieldno]) end
space:update(key, { {'=', 'information', information} })
updated = updated + 1
end
counter = counter + 1
if counter % 1000 == 0 then
fiber.yield()
end
if counter % 10000 == 0 then
log.info('%s: %s tuples have been checked in space %s', migration, counter, space_name)
end
end log.info('%s: %s tuples have been updated from space %s', migration, updated, space_name)
Here we are at the beginning
rename fields and assign nil to the old fields
update data according to the condition: if type = "01" or "02" or "03" or "04" and number = "YES", then add a new field status = "UPDATED" to each element of the users array
I wrote a part of the test that checks if there is a "company" space and if it contains an "information" map and if it contains an array "users" in which the fields from "rate" and "address" have been renamed to the new "user_rate" and "user_address".
I'm not sure I wrote the test correctly.
Also I'm missing part of the test for the second condition of the migration: adding a new field status = "UPDATED" under a certain condition.
local space_name = 'company'
g.before_all(function()
utils.init_config_loader(helper)
utils.apply_migrations_before(helper, '005')
end)
g.after_all(function()
utils.cleanup(helper)
end)
g.check_if_exist_space = function()
local operation = 'upsert'
utils.data_into_space(helper, space_name, 4, operation)
utils.apply_migrations(helper, { '005_update_company.lua' },
{ './migrations/005_update_company.lua' })
for index = 1, 4 do
local res, err = helper.cluster.main_server.net_box:eval([[
local router_helper = require('app.utils.router_helper')
local space_name, key, shard_value = ...
return crud.get(space_name, key, { fields = {'information'}})
]], { space_name, 'company_id' .. index })
t.assert_equals(err, nil)
t.assert_equals(#res.rows, 1)
t.assert_equals(#res.rows[1], 1)
local information = res.rows[1][1]
t.assert_equals(#information, 1)
t.assert_equals(information.users[1].rate, 'user_rate' .. index)
t.assert_equals(information.users[1].address, 'user_address' .. index)
end
end
Can you help me please?

ActiveSupport TimeZone not returning all zones

I'm having an issue like this. Not all zones are returning with:
ActiveSupport::TimeZone.all.sort_by {|t| t.name}.map { |tz|
#symbol = tz.tzinfo.identifier.gsub(/[^_a-zA-Z0-9]/, '_').squeeze('_').upcase!
tz.to_s #> (GMT+00:00) Edinburgh for example
}
I need to use the .to_s to get the UTC (GMT+00:00). With the above, London is missing and I assume others. This one works great:
ActiveSupport::TimeZone::MAPPING.sort_by {|k,v| k}.map { |k,v|
#symbol = k.gsub(/[^_a-zA-Z0-9]/, '_').squeeze('_').upcase!
k #> London London is included with this method
}
I cannot use this method because I do not know how to get the (GMT+00:00) in (GMT+00:00) London
Has the bug return? How to get all the zones show for the first example?
Edit.
I'm using GraphQL-ruby. I've created an enum to return a list of time zones:
# Taken from: https://gist.github.com/pedrocarmona/f41d25e631c1144045971c319f1c9e17
class Types::TimeZoneEnumType < Types::BaseEnum
ActiveSupport::TimeZone.all.sort_by {|t| t.name}.map { |tz|
symbol = tz.tzinfo.identifier.gsub(/[^_a-zA-Z0-9]/, '_').squeeze('_').upcase
value("TZ_#{symbol}", tz.to_s)
}
end
Then inside query_type.rb
[..]
field :time_zones, Types::TimeZoneEnumType, null: false
[..]
Next, inside graphiql, I make the query:
query timeZones{
__type(name: "TimeZoneEnum") {
enumValues {
name
description
}
}
}
Which returns something like, except London:
[
[..]
{
"name": "TZ_AMERICA_LA_PAZ",
"description": "(GMT-04:00) La Paz"
},
{
"name": "TZ_AMERICA_LIMA",
"description": "(GMT-05:00) Lima"
},
{
"name": "TZ_EUROPE_LISBON",
"description": "(GMT+00:00) Lisbon"
},
{
"name": "TZ_EUROPE_LJUBLJANA",
"description": "(GMT+01:00) Ljubljana"
},
{
"name": "TZ_EUROPE_MADRID",
"description": "(GMT+01:00) Madrid"
},
[..]
]
After Ljubljana I should see "London" but it's not there.
If I run
ActiveSupport::TimeZone.all.sort_by {|t| t.name}.map { |tz|
[ tz.tzinfo.identifier.gsub(/[^_a-zA-Z0-9]/, '_').squeeze('_').upcase, tz.to_s ]
}.sort
the result includes the entries ["EUROPE_LONDON", "(GMT+00:00) Edinburgh"], ["EUROPE_LONDON", "(GMT+00:00) London"], i.e. EUROPE_LONDON is duplicated.
I don't know how the GraphQL library is operating, but I'm assuming it's deduplicating the data and returning a single entry for EUROPE_LONDON (enums are normally unique). Moscow is the same - it has values for Moscow and St Petersburg - so you could test by looking at the results for EUROPE_MOSCOW.

Search a JSON Response using Ruby

I'm using a Ruby script to interface with an application API and the results being returned are in a JSON format. For example:
{
"incidents": [
{
"number": 1,
"status": "open",
"key": "abc123"
}
{
"number": 2,
"status": "open",
"key": "xyz098"
}
{
"number": 3,
"status": "closed",
"key": "lmn456"
}
]
}
I'm looking to search each block for a particular "key" value (yzx098 in this example) and return the associated "number" value.
Now, I'm very new to Ruby and I'm not sure if there's already a function to help accomplish this. However, a couple days of scouring the Googles and Ruby resource books hasn't yielded anything that works.
Any suggestions?
First of all, the JSON should be as below: (note the commas)
{
"incidents": [
{
"number": 1,
"status": "open",
"key": "abc123"
},
{
"number": 2,
"status": "open",
"key": "xyz098"
},
{
"number": 3,
"status": "closed",
"key": "lmn456"
}
]
}
Strore the above json in a variable
s = '{"incidents": [{"number": 1,"status": "open","key": "abc123"},{"number": 2,"status": "open","key": "xyz098"},{"number": 3,"status": "closed","key": "lmn456"}]}'
Parse the JSON
h = JSON.parse(s)
Find the required number using map
h["incidents"].map {|h1| h1['number'] if h1['key']=='xyz098'}.compact.first
Or you could also use find as below
h["incidents"].find {|h1| h1['key']=='xyz098'}['number']
Or you could also use select as below
h["incidents"].select {|h1| h1['key']=='xyz098'}.first['number']
Do as below
# to get numbers from `'key'`.
json_hash["incidents"].map { |h| h['key'][/\d+/].to_i }
json_hash["incidents"] - will give you the value of the key "incidents", which is nothing but an array of hash.
map to iterate thorough each hash and collect the value of 'key'. Then applying Hash#[] to each inner hash of the array, to get the value of "key". Then calling str[regexp], to get only the number strings like '098' from "xyz098", finally applying to_i to get the actual integer from it.
If the given hash actually a json string, then first parse it using JSON::parse to convert it to a hash.Then do iterate as I said above.
require 'json'
json_hash = JSON.parse(json_string)
# to get values from the key `"number"`.
json_hash["incidents"].map { |h| h['number'] } # => [1, 2, 3]
# to search and get all the numbers for a particular key match and take the first
json_hash["incidents"].select { |h| h['key'] == 'abc123' }.first['number'] # => 1
# or to search and get only the first number for a particular key match
json_hash["incidents"].find { |h| h['key'] == 'abc123' }['number'] # => 1

Stripe_ruby Unable to store card_last4 and card_type after successful customer creation

After creating a customer successfully, I can inspect the object with:
Rails.logger.debug("single card object has: #{customer.cards.data.card.inspect}")
which returns a json like this:
#<Stripe: : Customer: 0x2801284>JSON: {
"id": "cus_2WXxmvhBJgSmNY",
"object": "customer",
"cards": {
"object": "list",
"data": [
{
"id": "card_2WXxcCsdY0Jjav",
"object": "card",
"last4": "4242",
"type": "Visa",
"exp_month": 1,
"exp_year": 2014,
}
]
},
"default_card": "card_2WXxcCsdY0Jjav"
}
But I will do Customer.cards.data.last4 it gives a NOMethodError.
If I remove the last4 and just call Customer.cards.data, it gives
#<Stripe: : Card: 0x1ed7dc0>JSON: {
"id": "card_2Wmd80yQ76XZKH",
"object": "card",
"last4": "4242",
"type": "Visa",
"exp_month": 1,
"exp_year": 2015,
}
Now I seem to have the direct card object but if I do
card = Customer.cards.data
self.last4 = card.last4
I still get a noMethodError
Here is shortened version of my model:
class Payment < ActiveRecord::Base
def create_customer_in_stripe(params)
if self.user.stripe_card_token.blank?
user_email = self.user.email
customer = Stripe::Customer.create(email: user_email, card: params[:token])
card = customer.cards.data
self.card_last4 = card.last4
self.card_type = card.type
self.card_exp_month = card.exp_month
self.card_exp_year = card.exp_year
self.user.save
end
self.save!
end
end
customer.cards, as the name implies, returns multiple cards in an array.
You can't call card accessor methods because you don't have a Stripe::Card object; you have an array of Stripe::Card objects. You need to either call customer.cards.first (the most likely answer) or iterate over the array for a specific card you're looking for.
Once you have a Stripe::Card object, all the accessor methods will work correctly.
cself.card_last4 = card.last4 should be self.card_last4 = card["last4"] as the gem itself doesn't have a last4 method when searching on github. I know i have to use Hash syntax.
I have a feeling that all of your methods on card will need this syntax.
EDit:
So it sounds like your model's last4 column is an integer, do card["last4"].to_i or change the migration to a string column in the DB.
card = Customer.cards.data
self.last4 = card[0].last4
how do you get the default active card in
rails "default_card": "card_2WXxcCsdY0Jjav" in the list of customer.cards?
is there a better way rather than to loop thru customer.cards to get it or even easier way?
Any pointers?
Hope this help someone who is wondering as well :- )
default_active_card = customer.cards.data.detect{|obj| obj[:id] == customer.default_card}

Resources