Ruby refactoring, can't seem to get the syntax right - ruby

I'm pretty new at Ruby and I've been asked to help out on a program. For some reason for the life of me I can't get my syntax right. If the agent relationship is none?, I want to unshift both the 'series_system_agent_record_creation_by_agent_relationship' and 'series_system_agent_record_ownership_relationship'. I'm using the && in my code but it's not doing what I need it to do so any help would be much appreciated!
if whitelisted['agent_relationships'].none? {|r| r['jsonmodel_type'] == 'series_system_agent_record_creation_by_agent_relationship'}
whitelisted['agent_relationships'].unshift({
'jsonmodel_type' => 'series_system_agent_record_creation_by_agent_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => json.creating_agency[0]['ref'],
'relator' => 'created_by',
'start_date' => json.creating_agency[0]['start_date'],
})
end
if whitelisted['agent_relationships'].none? {|r| r['jsonmodel_type'] == 'series_system_agent_record_ownership_relationship'}
whitelisted['agent_relationships'].unshift({
'jsonmodel_type' => 'series_system_agent_record_ownership_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => json.responsible_agency['ref'],
'relator' => 'is_controlled_by',
'start_date' => json.responsible_agency['start_date'],
})
end
whitelisted['responsible_agency'] = json.responsible_agency
whitelisted['creating_agency'] = json.creating_agency
Sorry just a quick edit. To make sense of this the .none? is because I'm using an outdated version of Ruby for an older program. Not something I can change because it isn't my choice. To add I want to refactor this code. I know it already works. I've already tried a few ways and my head can't wrap my head around how to do it. Eg. I tried
if whitelisted['agent_relationships'].none? {|r| r['jsonmodel_type'] == 'series_system_agent_record_creation_by_agent_relationship' && 'series_system_agent_record_creation_by_agent_relationship'}
whitelisted['agent_relationships'].unshift({
'jsonmodel_type' => 'series_system_agent_record_creation_by_agent_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => json.creating_agency[0]['ref'],
'relator' => 'created_by',
'start_date' => json.creating_agency[0]['start_date'],
} && {
'jsonmodel_type' => 'series_system_agent_record_creation_by_agent_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => json.creating_agency[0]['ref'],
'relator' => 'created_by',
'start_date' => json.creating_agency[0]['start_date'],
}
)
end
whitelisted['responsible_agency'] = json.responsible_agency
whitelisted['creating_agency'] = json.creating_agency
Hello again! I've now also tried another method as well and I am not running into any errors however something seems to be wrong as this program is whitelisting relationships that can be passed to a react front end. Now none of the relationships are appearing on the react front end using the code below and although I am not running into any errors, it is still not working.
if whitelisted['agent_relationships'].none? {|r| r['jsonmodel_type'] == 'series_system_agent_record_ownership_relationship' && 'series_system_agent_record_creation_by_agent_relationship'}
[{
'jsonmodel_type' => 'series_system_agent_record_ownership_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => json.responsible_agency['ref'],
'relator' => 'is_controlled_by',
'start_date' => json.responsible_agency['start_date'],
},{
'jsonmodel_type' => 'series_system_agent_record_creation_by_agent_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => json.creating_agency[0]['ref'],
'relator' => 'created_by',
'start_date' => json.creating_agency[0]['start_date'],
},].each { |hsh| ['agent_relationships'].unshift(hsh) }
end
whitelisted['responsible_agency'] = json.responsible_agency
whitelisted['creating_agency'] = json.creating_agency

From what I understand from initial code you want to add series_system_agent_record_creation_by_agent_relationship default only if it's missing in whitelist. Same with series_system_agent_record_ownership_relationship - add only when it's missing.
However your refactoring tries to add both at a time.
Refactoring is usually a process when you refactor little bit and then it gives you idea what else can be refactored.
I would start with replacing long names.
One of the initial versions could be.
def whatever_the_name
# ...
unless has_agent_relationship_of_type?(whitelisted, 'series_system_agent_record_creation_by_agent_relationship')
creating_agency = json.creating_agency[0]
default_agent_relationship = {
'jsonmodel_type' => 'series_system_agent_record_creation_by_agent_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => creating_agency['ref'],
'relator' => 'created_by',
'start_date' => creating_agency['start_date'],
}
whitelisted['agent_relationships'].unshift(default_agent_relationship)
end
unless has_agent_relationship_of_type?(whitelisted, 'series_system_agent_record_ownership_relationship')
responsible_agency = json.responsible_agency
default_agent_relationship = {
'jsonmodel_type' => 'series_system_agent_record_ownership_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => responsible_agency['ref'],
'relator' => 'is_controlled_by',
'start_date' => responsible_agency['start_date'],
}
whitelisted['agent_relationships'].unshift(default_agent_relationship)
end
# ...
end
def has_agent_relationship_of_type?(whitelisted, type)
whitelisted['agent_relationships'].any? {|r| r['jsonmodel_type'] == type }
end
Of course, this shouldn't be considered the final refactored code. It all depends how far you want to get. But hopefully it helps.

Related

How to skip validation for array that is empty?

I have an incoming post data from js like this
[form] => Array (
[name] => 'a form'
[type] => 'form'
...
[children] => Array (
[0] =>
[1] =>
[2] => Array(
[title] => 'first'
[order] => '1'
...
}
[3] => Array(
[title] => 'second'
[order] => '2'
...
)
...
)
...
)
and rules like
[
'form.name' => 'required|string',
'form.type' => 'required|string',
...
'form.children.*.title' => 'requered|string'
'form.children.*.order' => 'requered|integer'
...
]
What is the best way to completely exclude/skip the form.children arrays that are empty and process the ones with data?
Try this:
[
'form.name' => 'required|string',
'form.type' => 'required|string',
...
'form.children.*.title' => 'sometimes|string'
'form.children.*.order' => 'sometimes|integer'
...
]
Sometimes means, if there is something, follow the next rule(s).
https://laravel.com/docs/8.x/validation#validating-when-present
Addition:
For more complex situations, f.ex. you are not interested in an order value if there is no title, makes sense right? Try this:
[
'form.name' => 'required|string',
'form.type' => 'required|string',
...
'form.children.*.title' => 'sometimes|string'
'form.children.*.order' => 'exclude_if:form.children.*.title,null|integer'
...
]
I have never tested/used this on arrays though.
I managed to completely remove the empty arrays just by filtering them out of the request with laravel's prepareForValidation() method and array_filter(). Worked great for me.
protected function prepareForValidation()
{
$this->merge([
"form" => [
"children" => array_filter($this->form["children"])
]
]);
}
#DimitriMostrey's answer worked as well. His answer is a shorter solution without an additional method. Will accept his answer so anyone facing a similar situation can pick the one that suits the most.

Laravel Error Update with foreach : syntax error, unexpected ' ' (T_STRING), expecting ']'

I have DB in SQL Server and I execute it with Laravel. However, I get the following error.
ParseError syntax error, unexpected ' ' (T_STRING), expecting ']'
I am trying to figure out why this is happening. It shows this syntax error, but I don't understand it. I can't find a typo. I read and update SQL Server database. Is there a bug or something?
$siswa_regNo = DB::table('upload_tbpendaftaran$')
->select('Reg_No', 'F_Name',
'N_Name',
'Place',
'Birthday',
'BirthID',
'Gender',
'Bloods',
'Glass',
'Height',
'Weight',
'Child_No',
'Child_Qty',
'School_Code',
'School_Name',
'School_Address',
'School_Distric',
'School_Distric_Desc',
'School_City',
'School_City_Desc',
'School_Province',
'School_Province_Desc',
'School_NEM_Avg',
'School_STTB',
'Certificate_ID',
'Stay',
'Stay_Address',
'Stay_RT',
'Stay_RW',
'Stay_Village',
'Stay_Distric',
'Stay_Distric_Desc',
'Stay_City',
'Stay_City_Desc',
'Stay_Province',
'Stay_Province_Desc',
'Stay_Phone',
'Stay_HP',
'Stay_Postal',
'Stay_Longitude',
'Stay_Latitude',
'Home_Address',
'Home_RT',
'Home_RW',
'Home_Village',
'Home_Distric',
'Home_Distric_Desc',
'Home_City',
'Home_City_Desc',
'Home_Province',
'Home_Province_Desc',
'Home_Postal',
'Home_Phone',
'Home_HP',
'Home_HP2',
'Home_Longitude',
'Home_Latitude',
'EMail',
'Email_Parent',
'Email_Parent2',
'ID_Cards',
'Distance',
'Time_HH',
'Time_MM',
'TransportID',
'TransportDesc',
'Remarks',
'NISN',
'ExamUNNo')
->orderBy('Reg_No')
->get();
foreach ($siswa_regNo as $s) {
DB::table('Registration')
->where('Reg_No', $s->Reg_No)
->update([
'F_Name' => $s->F_Name,
'N_Name' => $s->N_Name,
'Place' => $s->Place,
'Birthday' => $s->Birthday,
'BirthID' => $s->BirthID,
'Gender' => $s->Gender,
'Religion' => $s->Religion,
'Citizen' => $s->Citizen,
'FamilyID' => $s->FamilyID,
'Bloods' => $s->Bloods,
'Glass' => $s->Glass,
'Height' => $s->Height,
'Weight' => $s->Weight,
'Child_No' => $s->Child_No,
'Child_Qty' => $s->Child_Qty,
'School_Code' => $s->School_Code,
'School_Name' => $s->School_Name,
'School_Address' => $s->School_Address,
'School_Distric' => $s->School_Distric,
'School_Distric_Desc' => $s->School_Distric_Desc,
'School_City' => $s->School_City,
'School_City_Desc' => $s->School_City_Desc,
'School_Province' => $s->School_Province,
'School_Province_Desc' => $s->School_Province_Desc,
'School_NEM_Avg' => $s->School_NEM_Avg,
'School_STTB' => $s->School_STTB,
'Certificate_ID' => $s->Certificate_ID,
'Stay' => $s->Stay,
'Stay_Address' => $s->Stay_Address,
'Stay_RT' => $s->Stay_RT,
'Stay_RW' => $s->Stay_RW,
'Stay_Village' => $s->Stay_Village,
'Stay_Distric' => $s->Stay_Distric,
'Stay_Distric_Desc' => $s->Stay_Distric_Desc,
'Stay_City' => $s->Stay_City,
'Stay_City_Desc' => $s->Stay_City_Desc,
'Stay_Province' => $s->Stay_Province,
'Stay_Province_Desc' => $s->Stay_Province_Desc,
'Stay_Phone' => $s->Stay_Phone,
'Stay_HP' => $s->Stay_HP,
'Stay_Postal' => $s->Stay_Postal,
'Stay_Longitude' => $s->Stay_Longitude,
'Stay_Latitude' => $s->Stay_Latitude,
'Home_Address' => $s->Home_Address,
'Home_RT' => $s->Home_RT,
'Home_RW' => $s->Home_RW,
'Home_Village' => $s->Home_Village,
'Home_Distric' => $s->Home_Distric,
'Home_Distric_Desc' => $s->Home_Distric_Desc,
'Home_City' => $s->Home_City,
'Home_City_Desc' => $s->Home_City_Desc,
'Home_Province' => $s->Home_Province,
'Home_Province_Desc' => $s->Home_Province_Desc,
'Home_Postal' => $s->Home_Postal,
'Home_Phone' => $s->Home_Phone,
'Home_HP' => $s->Home_HP,
'Home_HP2' => $s->Home_HP2,
'Home_Longitude' => $s->Home_Longitude,
'Home_Latitude' => $s->Home_Latitude,
'EMail' => $s->EMail,
'Email_Parent' => $s->Email_Parent,
'Email_Parent2' => $s->Email_Parent2,
'ID_Cards' => $s->ID_Cards,
'Distance' => $s->Distance,
'Time_HH' => $s->Time_HH,
'Time_MM' => $s->Time_MM,
'TransportID' => $s->TransportID,
'TransportDesc' => $s->TransportDesc,
'Remarks' => $s->Remarks,
'NISN' => $s->NISN,
'ExamUNNo' => $s->ExamUNNo
]);
}
I am very confused, please help.
in second FOREACH, when you are getting data from DB you should add ->first() or ->get() in the last.
foreach ($siswa_regNo as $s) {
DB::table('Registration')
->where('Reg_No', $s->Reg_No)
->first() // this line added
->update([
'F_Name' => $s->F_Name,
'N_Name' => $s->N_Name,
'Place' => $s->Place,
.
.
.
]);
}
or if you want to use ->get() you should foreach to this also...

Extract filebeat prospector 'fileds' used from all attributes definitions (ruby based)

I'm currently adding filebeat support for services we provide;
For every service we have several log files I would like to track;
I have 1 common filebeat recipe and I'm distinguishing between the different services' logs in different attributes .rb files;
In those I have a separate definition per log;
All definitions have the same "fileds" configuration;
Can I add it to someplace to be used by all configurations?
My structure:
cookbooks
common
recipes
filebeat.rb
services
attributes
service1.rb
service2.rb
The content of servicesX.rb has the following definitions:
access_log = {
'paths' => ['TBD'],
'input_type' => 'log',
'fields' => {
'hostname' => node["opsworks"]["instance"]["hostname"],
'customer' => node["opsworks"]["instance"]["layers"][0],
'internal_ip' => node["opsworks"]["instance"]["private_ip"],
'ec2id' => node["opsworks"]["instance"]["aws_instance_id"],
'os' => node["opsworks"]["instance"]["os"],
'instance_type' => node["opsworks"]["instance"]["instance_type"] },
'fields_under_root' => true
}
audit_log = {
'paths' => ['TBD'],
'input_type' => 'log',
'fields' => {
'hostname' => node["opsworks"]["instance"]["hostname"],
'customer' => node["opsworks"]["instance"]["layers"][0],
'internal_ip' => node["opsworks"]["instance"]["private_ip"],
'ec2id' => node["opsworks"]["instance"]["aws_instance_id"],
'os' => node["opsworks"]["instance"]["os"],
'instance_type' => node["opsworks"]["instance"]["instance_type"]
},
'fields_under_root' => true
}
How can I extract
'fields' => {
'hostname' => node["opsworks"]["instance"]["hostname"],
'customer' => node["opsworks"]["instance"]["layers"][0],
'internal_ip' => node["opsworks"]["instance"]["private_ip"],
'ec2id' => node["opsworks"]["instance"]["aws_instance_id"],
'os' => node["opsworks"]["instance"]["os"],
'instance_type' => node["opsworks"]["instance"]["instance_type"]
Someplace in the same file (servicesX.rb) so to be used by all log files definitions?
Note: I'm a ruby novice :/
Thank you!!
After feedback and clarification in comments below, OP seems to want to DRY the code and re-use a fields definition.
Simplest is to store it in a variable and then use that:
fields = {
'hostname' => node["opsworks"]["instance"]["hostname"],
'customer' => node["opsworks"]["instance"]["layers"][0],
'internal_ip' => node["opsworks"]["instance"]["private_ip"],
'ec2id' => node["opsworks"]["instance"]["aws_instance_id"],
'os' => node["opsworks"]["instance"]["os"],
'instance_type' => node["opsworks"]["instance"]["instance_type"]
}
audit_log = {
'paths' => ['TBD'],
'input_type' => 'log',
'fields' => fields
}
This, however, may cause issues with how node is set. It really depends on the flow of the rest of your script. In chef (assuming this is about chef) a node is the context on which the script runs, so setting the fields too early might give issues when that node is used much later:
fields = { hostname: node["opsworks"]["instance"]["hostname"] }
# ... do lots of stuff, like fetching, preparing, connecting and whatnot.
fields # now contains the `node` values as set before connecting etc.
If this is an issue, a better option would be to define a method that returns the fields from a passed-in node:
def fields(node)
{
'hostname' => node["opsworks"]["instance"]["hostname"],
'customer' => node["opsworks"]["instance"]["layers"][0],
'internal_ip' => node["opsworks"]["instance"]["private_ip"],
'ec2id' => node["opsworks"]["instance"]["aws_instance_id"],
'os' => node["opsworks"]["instance"]["os"],
'instance_type' => node["opsworks"]["instance"]["instance_type"]
}
end
Or, cleaned up:
def fields(node)
instance = node["opsworks"]["instance"]
{
hostname: instance["hostname"],
customer: instance["layers"][0],
internal_ip: instance["private_ip"],
ec2id: instance["aws_instance_id"],
os: instance["os"],
instance_type: instance["instance_type"]
}
end
Then use that function:
audit_log = {
'paths' => ['TBD'],
'input_type' => 'log',
'fields' => fields(node)
}
After my research: The benefits of the implementation does not justify the time invested;
Leaving it;

Savon: Wrong Element Name Prefix. How can I change it?

So here is the code that I have:
resp = client.call(
:producer_query,
message: {
'cmn:Carrier' => '',
'cmn:ProducerCriteria' => { 'cmn:EntityType' => 'Individual',
'cmn:CustomerId' => 5555,
:attributes! => {'CustomerId' => {'type' => 'AGENTCD'}}},
'cmn:SectionConfiguration' => { 'cmn:SectionType' => 'Associations', :attributes! => {'cmn:SectionType' => {'activeOnly' => 'false'}}},
:attributes! => { 'cmn:Carrier' => { "id" => 55555 }}
}
) do
wsse_auth ENV['ID'], ENV['PASSWORD'], :digest
end
And it produces something that looks like:
<soapenv:Body>
<cmn:ProducerQuery>
<cmn:Carrier id="5555"/>
<cmn:ProducerCriteria>
<cmn:EntityType>Individual</cmn:EntityType>
<cmn:CustomerId>55555</cmn:CustomerId>
</cmn:ProducerCriteria>
<cmn:SectionConfiguration>
<cmn:SectionType activeOnly="false">Associations</cmn:SectionType>
</cmn:SectionConfiguration>
</cmn:ProducerQuery>
</soapenv:Body>
I've added in the 'cmn' to everything, but the ProducerQuery to make it match what I think it should be. However, I think it really should read 'tran' instead. I can control the 'cmn' for everything but the part that reads <cmn:ProducerQuery>. How can I make it read <cmn:ProducerQuery>?

Adapter pattern for Gem that uses multiple composed classes?

I'm using the Axlsx gem (https://github.com/randym/axlsx) for generating excel files. I want to refactor my code so that I have a wrapper around the gem just in case I want to switch gems.
Going through this reminds me of the Adapter design pattern. However, there are just so many objects nested underneath the main Package object that I am getting confused as to how to actual create an adapter for it. Ex:
You create a Package object
You access the Workbook within this Package objet
You access the Sheet from the Workbook object, and vice versa
Here are some of my classes:
class ReportGenerator::Base
...
def create_workbook
...
#package = Axlsx::Package.new <---------------------------
#workbook = #package.workbook <---------------------------
#workbook.use_shared_strings = true
end
class Sheet::Base
def initialize(workbook, question, options = {})
...
#sheet = workbook.add_worksheet(:name => sheet_name) <---------------------------
end
def styles
#styles ||= {
"title" => #sheet.workbook.styles.add_style(:sz => 20, :b => true, :alignment => { :wrap_text => true }),
"bold" => #sheet.workbook.styles.add_style(:b => true),
"header" => #sheet.workbook.styles.add_style(:fg_color => "FFFFFF", :bg_color => "ff3333", :sz => 12, :b => true, :alignment => {:horizontal => :center}, :border => {:style => :thin, :color => "FFFFFF"}),
"subheader" => #sheet.workbook.styles.add_style(:fg_color => "FFFFFF", :bg_color => "ff3333", :sz => 12, :b => true, :alignment => {:horizontal => :center}),
"subheader_border_left" => #sheet.workbook.styles.add_style(:fg_color => "FFFFFF", :bg_color => "ff3333", :sz => 12, :b => true, :alignment => {:horizontal => :center}, :border => {:style => :thin, :color => "FFFFFF", :edges => [:left]}),
"blue_link" => #sheet.workbook.styles.add_style(:fg_color => '0000FF'),
"wrap_text" => #sheet.workbook.styles.add_style(:alignment => { :wrap_text => true, :horizontal => :left }),
"percentage" => #sheet.workbook.styles.add_style(:format_code => "0.00%")
}
end
Here's my first stab:
class ExcelWriter
def initialize
#package = Axlsx::Package.new
end
def workbook
#package.workbook
end
# starting to feel like it's not doable within one class..?
end
There are so many classes involved that it feels like I can't wrap everything into one adapter? Or maybe I'm doing it wrong? Any tips would be welcomed.
Focus on what you're actually using and not on existing infrastracture of Axlsx gem. This way you may combine work of several Axlsx objects into 1 method call.
I don't know what you're actually using, so it's hard for me to say which objects do you need.

Resources