Merge multiple hashes after new key - ruby

I've got three hashes which I want to merge to base_options under new key - checks. Basically what I want to achieve is:
{
base_options,
checks: {
document_check,
identity_check,
dummy_check,
}
},
Below sample hash data:
dummy_check = {
dummy: {
enabled: true,
preferences: {
state: 0,
replay: true,
},
}
}
identity_check = {
identity: {
enabled: true,
preferences: {},
},
}
document_check = {
document: {
enabled: true,
preferences: {
face: false,
liveness: false,
docs_all: true,
},
},
}
base_options = {
send_email: true,
send_reminder: false,
reset_client_status: true,
}
So if I do base_options.merge!(checks: document_check.merge!(identity_check, dummy_check)) I will receive expected hash which is:
{
send_email: true,
send_reminder: false,
reset_client_status: true,
checks: {
document: {
...
},
identity: {
...
},
dummy: {
...
}
},
}
But this is not super flexible and I don't know if using .merge! two times in one line is not a crap. Are there any other alternatives?
I'm using Ruby 2.7 and Rails 6

Using merge! is fine and well understood. However, as you are setting a single key on your base_options hash, you can also simple use the hash accessor, i.e.
base_options[:checks] = document_check.merge!(identity_check, dummy_check)
Note that this will also change the document_hash object as merge! modified the receiver. If this is not desired, you can also use merge and return a new Hash. Thus could look like:
base_options[:checks] = document_check.merge(identity_check, dummy_check)
or equivalently
base_options[:checks] = {}.merge!(document_check, identity_check, dummy_check)
The latter option is slightly slower but might better show your intended behavior and is thus easier to understand to readers of your code.

If I understand correctly, you can try the Double Splat **. you can use like this:
base_options.merge(
checks: **document_check, **identity_check, **dummy_check
)

The answer is in your question, below can be a simple way of achieving the end result.
Below is after initializing values of base_options, document_check, identity_check,dummy check.
base_options = {
base_options: base_options,
checks: {
document_check: document_check,
identity_check: identity_check,
dummy_check: dummy_check,
}
}
=> {:base_options=>{:send_email=>true, :send_reminder=>false, :reset_client_status=>true}, :checks=>{:document_check=>{:document=>{:enabled=>true, :preferences=>{:face=>false, :liveness=>false, :docs_all=>true}}}, :identity_check=>{:identity=>{:enabled=>true, :preferences=>{}}}, :dummy_check=>{:dummy=>{:enabled=>true, :preferences=>{:state=>0, :replay=>true}}}}}

Related

GraphQL Apollo pagination and type policies

I am really struggling with this concept. I hope someone can help me understand it better.
The documentations uses a simple example and it's not 100% clear to me how it works.
I have tried using keyArgs, but they didn't work, so I adopted to use the args parameter in the read and merge functions. First, let me explain my scenario.
I have a couple of search endpoints that use the same parameters:
{
search:
{
searchTerm: "*",
includePartialMatch: true,
page: 1,
itemsToShow: 2,
filters: {},
facets: [],
orderBy: {}
}
}
So I have setup my type policies like this:
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
searchCategories: typePolicy,
searchBrands: typePolicy,
searchPages: typePolicy,
searchProducts: typePolicy,
},
},
},
});
And I was using a generic typePolicy for them all.
At first, I tried this:
const typePolicy = {
keyArgs: [
"search",
[
"identifier",
"searchTerm",
"includePartialMatches",
"filters",
"orderBy",
"facets",
],
],
// Concatenate the incoming list items with
// the existing list items.
merge(existing: any, incoming: any) {
console.log("existing", existing);
console.log("incoming", incoming);
if (!existing?.items) console.log("--------------");
if (!existing?.items) return { ...incoming }; // First request
const items = existing.items.concat(incoming.items);
const item = { ...existing, ...incoming };
item.items = items;
console.log("merged", item);
console.log("--------------");
return item;
},
};
But this does not do what I want.
What I would like, is for apollo to work as it does normally, but when the "page" changes for any field, it appends it instead of caching a new request.
Does anyone know what I am doing wrong or can provide me with a better example that what is on the documentation?

Request : duplicateSheet with Google Spreadsheet API : badRequest: Must specify at least one request

I'm trying to duplicate a sheet using Google Spreadsheet API.
But I keep getting this error : badRequest: Must specify at least one request
I've tried a lot of things but nothing seems to work so far.
Here is what I have (ruby) :
request_body = Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest.new {
{
"includeSpreadsheetInResponse": false,
"requests": [
{
"duplicateSheet": {
"sourceSheetId": 1*********,
"insertSheetIndex": 2,
"newSheetId": 10,
"newSheetName": "*********"
}
}
],
"responseIncludeGridData": false,
"responseRanges": [
""
]}
}
response = service.batch_update_spreadsheet(spreadsheet_id, request_body)
I know the code is not over but I really can't figure out what is missing
Does anyone know what I need ? Many thanks in advance !!!
The new object should be enclosed with open and close parenthesis.
Your code should look like this:
request_body = Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest.new(
{
"includeSpreadsheetInResponse": false,
"requests": [
{
"duplicateSheet": {
"sourceSheetId": 1*********,
"insertSheetIndex": 2,
"newSheetId": 10,
"newSheetName": "*********"
}
}
],
"responseIncludeGridData": false,
"responseRanges": [
""
]}
)
Reference:
Ruby Object and Classes
In your script, you use the camel case. In the case of Ruby, please use the snake case as follows.
Modified script:
request_body = Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest.new(
{
include_spreadsheet_in_response: false,
requests: [
{
duplicate_sheet: {
source_sheet_id: 1*********,
insert_sheet_index: 2,
new_sheet_id: 10,
new_sheet_name: "*********",
}
}
],
response_include_grid_data: false,
response_ranges: [""]
})
response = service.batch_update_spreadsheet(spreadsheet_id, request_body)
Note:
As other patterns, you can also use the following scripts.
Pattern 2
request = Google::Apis::SheetsV4::Request.new
request.duplicate_sheet = {
source_sheet_id: 1*********,
insert_sheet_index: 2,
new_sheet_id: 10,
new_sheet_name: "*********",
}
request_body = Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest.new
request_body.include_spreadsheet_in_response = false
request_body.response_include_grid_data = false
request_body.response_ranges = [""]
request_body.requests = [request]
response = service.batch_update_spreadsheet(spreadsheet_id, request_body)
Pattern 3
request_body = {
include_spreadsheet_in_response: false,
requests: [{duplicate_sheet: {
source_sheet_id: 1*********,
insert_sheet_index: 2,
new_sheet_id: 10,
new_sheet_name: "*********",
}}],
response_include_grid_data: false,
response_ranges: [""],
}
response = service.batch_update_spreadsheet(spreadsheet_id, request_body, {})
Note:
In this answer, it supposes that your service can be used for using the batchUpdate method. Please be careful this.
Reference:
Method: spreadsheets.batchUpdate

Mongoose make Array required

I've got a mongoose model that looks something like this:
var ProjectSchema = new Schema({
name: { type: String, required: true },
tags: [{ type: String, required: true }]
});
I want it to be required for a project to have at least one tag. However when I save a new project without a tags array, mongoose does not throw an error:
var project = new Project({'name': 'Some name'});
project.save(function(err, result) {
// No error here...
});
What am I missing here? How can I specify an array to be required?
Mongoose 5.x
https://mongoosejs.com/docs/migrating_to_5.html#array-required
tags: {
type: [String],
validate: v => Array.isArray(v) && v.length > 0,
}
Mongoose 4.x
One-liner would be:
tags: {type: [String], required: true}
SchemaTypes
AFAIK, you need to set the type to Array and add a custom validator to make sure that each entry is a String:
tags : {
type : Array,
required : true,
validate : {
validator : function(array) {
return array.every((v) => typeof v === 'string');
}
}
}
OK I tried a new method and it seems to work just fine for mongoose ^5.11.15 I'm not really sure if it's a proper answer in terms of clean code but as far as the functionality which is to make an array of Numbers/Strings required (meaning it won't accept an empty array) it works OK so it's as follows
size: [{
type: Number,
required: true
}],
instead of
size: {
type: [Number],
required: true
},
Instead of defining the type as an array of numbers/strings, I defined the size to be an array of numbers and then the required attribute works as it should it doesn't accept an empty array and raises an error to illustrate that.
again I'm not really sure if this is the best way to define a required array but as far as the functionality it works just fine

sortInfo does not work

I am trying to display data from table in sorted way. I want to display content ordered by creation date. I add sortInfo, but it does not work! I use angular ui-grid. Here is my code
$scope.gridOptions = {
enableSorting: true,
columnDefs: [
{ field: 'name'},
{ field: 'age'},
{ field: 'creationDate', cellFilter : "date:'yyyy-MM-dd'"}
],
sortInfo: {
fields: ['creationDate'],
directions:['desc']
}
};
Is it possible to set sort by default here? And how to do it?
I didn't found in ui-grid docs sortInfo option.
Your gridOptions is not set right. You need to add the sort property to your column definition like below, the priority is what makes it sort by default. Lower priority gets sorted first. Read more here http://ui-grid.info/docs/#/tutorial/102_sorting
$scope.gridOptions = {
enableSorting: true,
columnDefs: [
{
field: 'name',
sort: {
direction: uiGridConstants.DESC,
priority: 1
}
}
}

avoiding deep hash queries in ruby

How can I tidy up code that sets and requests deep hash values in ruby?
For example, say I have a hash like this:
hash = {
user_settings: {
notifications: {
overdue_tasks: { enabled: true, duration: 30 },
created_overdue_tasks: { enabled: true, duration: 30 }
}
}
}
How can I avoid writing brittle access code like this:
hash[:user_settings][:notifications][:overdue_tasks][:duration] = 5
Also, is there a recursive symbolize_keys that will symbolize all keys and not just the top level?
I don't know such way to reduce to code to fetch the desired key/value, but I have a suggestion to make your hash plain by naming.
How about
hash = {
user_settings: {
overdue_task_notification_enabled: true,
overdue_task_notification_duration: 30,
created_overdue_tasks_enabled: true,
created_overdue_tasks_duration: 30
}
}
Then fetch it like
hash[:user_settings][:created_overdue_tasks_duration]
I think this arrangement looks easier to understand for peers and users.
Taking the help from the blog :- Ruby Nested Hash - Deep Fetch - Returning a (Default) Value for a Key That Does Not Exist in a Nested Hash :
class Hash
def deep_fetch(key, default = nil)
default = yield if block_given?
(deep_find(key) or default) or raise KeyError.new("key not found: #{key}")
end
def deep_find(key)
key?(key) ? self[key] : self.values.inject(nil) {|memo, v| memo ||= v.deep_find(key) if v.respond_to?(:deep_find) }
end
end
hash = {
user_settings: {
notifications: {
overdue_tasks: { enabled: true, duration: 30 },
created_overdue_tasks: { enabled: true, duration: 30 }
}
}
}
p hash.deep_fetch(:duration)
# >> 30

Resources