alternative to using 'await' with lazy_static! macro in rust? - mongodb

I want to use Async MongoDB in a project.
I don't want to pass around the client because it would need to go around multiple tasks and threads. So I kept a static client using lazy_static. However, I can't use await in the initialization block.
What can I do to work around this?
Suggestions for doing it without lazy_static are also welcome.
use std::env;
use futures::stream::StreamExt;
use mongodb::{
bson::{doc, Bson},
options::ClientOptions,
Client,
};
lazy_static! {
static ref MONGO: Option<Client> = {
if let Ok(token) = env::var("MONGO_AUTH") {
if let Ok(client_options) = ClientOptions::parse(&token).await
^^^^^
{
if let Ok(client) = Client::with_options(client_options) {
return Some(client);
}
}
}
return None;
};
}

I went with this approach based on someone's suggestion in rust forums.
static MONGO: OnceCell<Client> = OnceCell::new();
static MONGO_INITIALIZED: OnceCell<tokio::sync::Mutex<bool>> = OnceCell::new();
pub async fn get_mongo() -> Option<&'static Client> {
// this is racy, but that's OK: it's just a fast case
let client_option = MONGO.get();
if let Some(_) = client_option {
return client_option;
}
// it hasn't been initialized yet, so let's grab the lock & try to
// initialize it
let initializing_mutex = MONGO_INITIALIZED.get_or_init(|| tokio::sync::Mutex::new(false));
// this will wait if another task is currently initializing the client
let mut initialized = initializing_mutex.lock().await;
// if initialized is true, then someone else initialized it while we waited,
// and we can just skip this part.
if !*initialized {
// no one else has initialized it yet, so
if let Ok(token) = env::var("MONGO_AUTH") {
if let Ok(client_options) = ClientOptions::parse(&token).await {
if let Ok(client) = Client::with_options(client_options) {
if let Ok(_) = MONGO.set(client) {
*initialized = true;
}
}
}
}
}
drop(initialized);
MONGO.get()
}

However I can't use await in the initialization block.
You can skirt this with futures::executor::block_on
use once_cell::sync::Lazy;
// ...
static PGCLIENT: Lazy<Client> = Lazy::new(|| {
let client: Client = futures::executor::block_on(async {
let (client, connection) = tokio_postgres::connect(
"postgres:///?user=ecarroll&port=5432&host=/run/postgresql",
NoTls,
)
.await
.unwrap();
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("connection error: {}", e);
}
});
client
});
client
});
What we have is a non-async closure blocking in a single thread until the resolution of the future.

Create a new runtime from tokio::runtime::Runtime and use block_on to block the current thread until completion.
// database.rs
use tokio::runtime::Runtime;
use mongodb::Client;
pub fn connect_sync() -> Client {
Runtime::new().unwrap().block_on(async {
Client::with_uri_str("mongodb://localhost:27017").await.unwrap()
})
}
// main.rs
mod database;
lazy_static! {
static ref CLIENT: mongodb::Client = database::connect_sync();
}
#[actix_web::main]
async fn main() {
let collection = &CLIENT.database("db_name").collection("coll_name");
// ...
}

Use the async_once crate.
use async_once::AsyncOnce;
use lazy_static::lazy_static;
use mongodb::Client;
lazy_static! {
static ref CLIENT: AsyncOnce<Client> = AsyncOnce::new(async {
Client::with_uri_str(std::env::var("MONGO_URL").expect("MONGO_URL not set"))
.await
.unwrap()
});
}
then
CLIENT.get().await;

Related

How can i make Http request again in retrofit to get updated data from api

I want to update the data I'm getting from api to display it in my lazy column, I'm trying to add swipe down to refresh functionality.
I'm getting the data from my viewmodel
#HiltViewModel
class MatchesViewModel #Inject constructor(
private val matchRepository: MatchRepository
): ViewModel() {
val response: MutableState<ApiState> = mutableStateOf(ApiState.Empty)
init {
getAllMatches()
}
private fun getAllMatches() = viewModelScope.launch {
cricketRepository.getAllMatches().onStart {
response.value = ApiState.Loading
} .catch {
response.value = ApiState.Failure(it)
}.collect {
response.value = ApiState.Success(it) }
}
}
then i made new kotlin file where I'm checking if I'm getting the data and passing it in my lazy column
#Composable
fun MainScreen(viewModel: MatchesViewModel = hiltViewModel()){
when (val result = viewModel.response.value){
is ApiState.Success -> {
HomeScreen(matches = result.data.data)
}
is ApiState.Loading -> {
}
is ApiState.Empty -> {
}
is ApiState.Failure -> {
}
}
}
i want to know how can i make the request again to get the updated data
after some googling i found out you can retry api calls with okhttp interceptors but could'nt find any documentation or tutorial to retry calls with interceptor
You can try Swipe to Refresh with the accompanist dependencie
implementation "com.google.accompanist:accompanist-swiperefresh:0.26.5-rc"
Implementation would be like this
val swipeRefreshState = rememberSwipeRefreshState(false)
SwipeRefresh(
state = swipeRefreshState,
onRefresh = {
swipeRefreshState.isRefreshing = false
viewModel.<FUNCTION TO LOAD YOUR DATA>
},
indicator = { swipeRefreshState, trigger ->
SwipeRefreshIndicator(
// Pass the SwipeRefreshState + trigger through
state = swipeRefreshState,
refreshTriggerDistance = trigger,
// Enable the scale animation
scale = true,
// Change the color and shape
backgroundColor = <UPDATE CIRLE COLOR>,
contentColor = <RELOADING ICON COLOR>,
shape = RoundedCornerShape(50),
)
}
) {
<CONTENT OF YOUR SCREEN>
}

TYpescript : Static methods on Function as class

I have a fn that inherit an existing fn ( take Angular1 $q for example )
//$q original behavior
var defer = $q.defer();
defer.promise.then(function(result){})
//or
$q( (resolve, reject) => {
//promise execution here
}).then(function(result){});
If I want to decorate it, I would do :
var Qdecorator = function($delegate) {
var Q = function(resolver:any): any {
//do some extra stuff here
return $delegate.apply($delegate, arguments);
}
//Assign the static methods here:
Q.defer = function() {
//do some stuff
return $delegate.defer.apply($delegate, []);
}
//same goes for race, when, resole reject and so on
return Q;
}
Problem is that typescript complains about
Property defer, race, when, resolve, etc... does not exist on type '(resolver: any) => any'
I tried to use the IQService, and IPromise with no luck, btu I'd like to raise a more global question :
How do I define late static methods on function() that return an object without using new
I am copying pasting the answer to my question from this link:
https://www.typescriptlang.org/docs/handbook/interfaces.html
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

How to pass a test if expect fails

I have this code
it('This should pass anyway', function (done) {
testObj.testIt(regStr);
});
testObj
this.testIt = function (regStr) {
selector.count().then(function (orgCount) {
for (var curr = 0; curr < count; curr++) {
checkField(curr, regStr);
}
});
};
function checkField(curr, regStr) {
selector.get(curr).all(by.tagName('li')).get(0).getInnerHtml().then(function (text) {
expect(text).to.match(regStr, curr + '#ERR');
});
}
If one of these expects get a failure, test fails. How can i handle this? I mean - can i somehow count passed and failed expect()ations and return it? or, at least, dont let test break on first error.
I've tried try-catch, but nothing good happened.
it('This should pass anyway', function (done) {
try {
testObj.testIt(regStr);
} catch (e) {
console.log('#err' + e);
}
});
And then i wanted to use done(), but havent found any examples to do the similar. Can u please help me?
Sry for my english
UPD
You can return either null or a string from checkField(), join them up, and expect the array to be empty:
this.testIt = function (regStr) {
selector.count().then(function (orgCount) {
var errors = [];
for (var curr = 0; curr < orgCount; curr++) {
var e = checkField(curr, regStr);
if (e) { errors.push(e); }
}
assert.equal(0, errors.length, errors);
});
};
A cleaner approach would be to use map() to collect the data into an array:
var data = selector.map(function (elm) {
return elm.element(by.tagName('li')).getText();
});
expect(data).toEqual(["test1", "test2", "test3"]);

Dart HTML Server. How do I have the server refuse more than one client connection?

I'm writing a web application that is to be used by only one client and want to accept only one connection to the server. The LAN will be confined to an aircraft. I'm really new to Dart, HTML etc. How can I refuse multiple connections to the server?
Here's my code fir the HTTP server -
class MicroServer {
var address;
var port;
var httpServer; // global
MicroServer(this.address, this.port) {
final HTTP_ROOT_PATH = Platform.script.resolve('../build/web').toFilePath();
final virDir = new VirtualDirectory(HTTP_ROOT_PATH)
..jailRoot = false
..allowDirectoryListing = true;
HttpServer.bind(address, port)
.then((httpserver) {
httpServer = httpserver;
httpserver.listen((request) {
virDir.serveRequest(request);
});
});
}
}
Dart is single-threaded, so you can safely use check a variable to see if there's a current connection:
bool hasClient = false;
HttpServer.bind(address, port)
.then((httpserver) {
httpServer = httpserver;
httpserver.listen((request) {
if (hasClient) {
sendBusyPage(request);
} else {
hasClient = true;
virDir.serveRequest(request);
hasClient = false;
}
});
});
}
I have found that the code below works so far. I could also have taken advantage of
if(request.session.isNew) // then grab the session.id etc.
but it's just as easy to use the session.id variable since I will use it anyway.
class MicroServer {
var address;
var port;
var httpServer;
var sessionID; // null until first request received
MicroServer(this.address, this.port) {
final HTTP_ROOT_PATH = Platform.script.resolve('../build/web').toFilePath();
final virDir = new VirtualDirectory(HTTP_ROOT_PATH)
..jailRoot = false // process links will work
..followLinks = true
..allowDirectoryListing = true;
HttpServer.bind(address, port)
.then((httpserver) {
httpServer = httpserver;
httpserver.idleTimeout = null;
print("micro server started on ${httpserver.address}:${httpserver.port}");
httpserver.listen((request) {
if(sessionID == null) {
sessionID = request.session.id;
virDir.serveRequest(request);
} else if(sessionID == request.session.id){
virDir.serveRequest(request);
} else {
request.response.writeln('ERROR - Connection is in use.');
request.response.close();
request.session.destroy();
}
});
}).catchError((e) => print(e.toString()));
}
}

knockout viewmodel and requirejs

I have recently started to work with requirejs and when I try to create a simple viewmodel I get an strange exception. The exception comes from the knockout-2.1.0.js file and the exception is "Only subscribable things can act as dependencies".
define("PageViewModel", ["knockout-2.1.0"], function(ko) {
return function PageViewModel() {
var self = this;
self.visiblePage = ko.observable("StartPage");
self.showPage = function (pageName) {
self.visiblePage(pageName);
};
};
});
As you can see the viewmodel is extremly simple and since the error is in the knockout js file, it seems like requirejs is working as it should. I have been looking at: http://knockoutjs.com/documentation/amd-loading.html
The exception occur when coming to the line: self.visiblePage = ko.observable("StartPage");
Any ideas on what I'm doing wrong?
Thanks,
Ludwig
Update:
This is the module containing the pageviewmodel:
define("ViewModelFactory", ["StorageService", "PageViewModel", "AddUnitViewModel", "AddRoomViewModel"],
function (StorageService, PageViewModel, AddUnitViewModel, AddRoomViewModel) {
//var repositoryStorage = new StorageService();
var createAddRoomVM = function () {
var vm = new AddRoomViewModel();
vm.setRepository = StorageService.getRoomRepository();
return vm;
};
var createAddUnitVM = function () {
var vm = new AddUnitViewModel();
vm.setRepository = StorageService.getUnitRepository();
return vm;
};
var createPageVM = function () {
var vm = new PageViewModel();
return vm;
};
return {
createPageVM:createPageVM,
createAddRoomVM: createAddRoomVM,
createAddUnitVM: createAddUnitVM
};
});
And the module calling the factory
define("ApplicationViewModel", ["ViewModelFactory"],
function (viewModelFactory) {
mainVM = null;
var initVM = function () {
mainVM = {
page: viewModelFactory.createPageVM(),
addRoom: viewModelFactory.createAddRoomVM(),
addUnit: viewModelFactory.createAddUnitVM()
};
};
var getVM = function (viewName) {
switch (viewName) {
case "AddRoom":
return mainVM.addRoom;
case "AddUnit":
return mainVM.addUnit;
default:
return null;
}
};
var getPageVM = function () {
return mainVM.page;
};
return {
initVM: initVM,
getVM: getVM,
getPageVM: getPageVM,
mainVM: mainVM
};
});
And the class containing the applicationViewModel:
define("Bootstrapper", ["knockout-2.1.0", "Routing", "ApplicationViewModel"],
function (ko, routing, applicationViewModel) {
var run = function () {
applicationViewModel.initVM(); <-- after here mainVM.page is null
var mainVM = applicationViewModel.mainVM;
routing.initRouting(applicationViewModel);
ko.applyBindings(mainVM);
routing.showView("StartPage");
alert("Start");
};
return {
run: run
};
})
Your problem may have been caused by Knockout 2.1, which didn't work well when ko was not a global variable.
Knockout 2.2 should work fine, and I see from your comment this did indeed fix the problem.