I am new to motoko. As i understand an Actor in motoko is consider as a class.And I want to create multiple object from that Actor. My question is am I right about it and if the answer is yes then how can i identify or control these object.
An actor is like a plain object. If you want to create multiple instances, then you need to define an actor class, which is like a regular class but produces actors:
actor class A(n : Nat) {
var x : Nat = n;
public func get() : Nat { x };
public func set(n : Nat) { x := n };
};
// ... elsewhere, spawn two actors:
// (needs await because actor creation is asynchronous)
let a1 = await A(1);
let a2 = await A(2);
// ... and use them:
let n = (await a1.get()) + (await a2.get());
According to the Motoko docs:
An actor is similar to an object, but is different in that its state is completely isolated, its interactions with the world are entirely through asynchronous messaging, and its messages are processed one-at-a-time, even when issued in parallel by concurrent actors
Related
I'm trying to find a streamlined way to keep members in about 100 Google groups up to date from info already in a Google Sheets workbook. One sheet has a list of 1700 students w/ columns for student email and group email. Another sheet contains a list of just the group names and the group emails. Based on the codes I found on it seems possible with Google apps script. Unfortunately, I have no previous experience with Apps Script and minimal experience with javascript. I've been trying to piece together a process that might look like this.
Deleting all current members of the groups.
Uploading the correct members.
Rinse and repeat.
Not the most elegant, but it seemed workable.
This is what I've done so far and I've hit a wall.
I found the combination of these 2 functions will remove all members from 1 group:
//Function to Remove 1 group member from 1 group
function removeGroupMember(groupEmail, userEmail) {
Logger.log(userEmail)
userEmail = userEmail.trim();
AdminDirectory.Members.remove(groupEmail, userEmail);
}
//Remove all Members from 1 group
function removeAllMembers() {
var groupEmail = 'groupemail#mydomain.org';
var members = AdminDirectory.Members.list(groupEmail).members;
for (var m in members) {
var member = members[m];
var email = member.email;
removeGroupMember(groupEmail, email)
}
}
I thought I could nest another loop to pair it with this code that grabs the group emails from the sheet.
//get all of the groups from the spreadsheet
function getGroupsFromSpreadsheet(){
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName('google_groups');
const data = sheet.getRange(2,2,100).getValues();
return data;
logger.log(data)
}
This is what I came up with after a few trial and errors. It runs from the script editor and the execution log says it's successful, but then nothing actually happens in Google Admin - they're all still in the groups.
function removeAllMembers() {
var groups = [getGroupsFromSpreadsheet()];
var groupEmail = [getGroupsFromSpreadsheet()];
var members = AdminDirectory.Members.list(groupEmail).members;
for (var element in groups) {
for (var m in members) {
var member = members[m];
var email = member.email;
removeGroupMember(groupEmail, email);
}
}
}
Untested as I don't have a G Suite account. Have never worked with AdminDirectory API, so I don't know if below is the best way to do this.
function* getGroupEmailsFromSpreadsheet() {
// Should allow caller to access each "group" with a
// simple for-of loop. Generator used here, but could
// have used anything that would flatten the nested array
// that Range.getValues() returns.
const ss = SpreadsheetApp.GetActiveSpreadsheet();
const sheet = ss.getSheetByName('google_groups');
const data = sheet.getRange(2, 2, 100).getValues();
for (let rowIndex = 0; rowIndex < data.length; ++rowIndex) {
for (let columnIndex = 0; columnIndex < data[0].length; ++columnIndex) {
// Maybe do some validation here e.g. making sure email is a string, contains an "#" character
yield data[rowIndex][columnIndex];
}
}
}
function* getAllMembersInGroup(groupKeyOrEmail) {
// Should allow caller to access each "member" with a simple
// for-of loop. Generator used here, so that call site
// doesn't need to deal with handling pagination.
const options = {
maxResults: 200
};
// https://developers.google.com/admin-sdk/directory/v1/reference/members/list#request
let groupMembers = AdminDirectory.Members.list(groupKeyOrEmail, options);
do {
for (const member of groupMembers.members) {
yield member;
}
options.pageToken = groupMembers.nextPageToken;
groupMembers = AdminDirectory.Members.list(groupKeyOrEmail, options)
} while (groupMembers.nextPageToken);
}
function removeAllMembersFromAllGroups() {
// Should remove all "members" in the "groups" on the sheet.
for (const groupEmail of getGroupEmailsFromSpreadsheet()) {
for (const member of getAllMembersInGroup(groupEmail)) {
Logger.log(`Going to try and remove member ${member.email} from group ${groupEmail}.`);
AdminDirectory.Members.remove(groupEmail, member.email);
}
}
}
Before running the removeAllMembersFromAllGroups function:
It would be good if you randomly picked one group (someGroup) from your worksheet, randomly picked one member in that group (someMember) and then create/run a temporary function containing AdminDirectory.Members.remove(someGroup, someMember) -- except replace someGroup and someMember with the actual values you've chosen. Then check if that member has actually been removed from the group. This should prove whether the code can actually delete members from groups (or whether there is a problem even with this simple scenario).
Double-check that the script has all necessary permissions granted.
Then set up a breakpoint inside the do-while loop in getAllMembersInGroup as the do-while loop is untested. Then debug the removeAllMembersFromAllGroups function. You'll want to check that the getAllMembersInGroup generates a finite sequence of "members" (and doesn't loop forever).
The issue in the code you have is in fact caused my the groups and groupEmail variables in the code. Therefore, I suggest you try this:
function getGroups() {
var ss = SpreadsheetApp.openById('SPREADSHEET_ID').getSheetByName('SHEET_NAME');
var groups = ss.getRange(2, 2, 2, 100).getValues();
return groups;
}
function removeGroupMember(groupEmail, userEmail) {
userEmail = userEmail.trim();
AdminDirectory.Members.remove(groupEmail, userEmail);
}
function removeAllMembers() {
var groups = getGroups();
for (var i = 0; i < groups.length; i++) {
var members = AdminDirectory.Members.list(theGroups[i][0]).members;
for (var j = 0; j < members.length; j++)
removeGroupMember(theGroups[i][0], members[j].email);
}
}
Reference
Range Class Apps Script - getValues;
SpreadsheetApp Class Apps Script - openById() .
I wanted the scene load 5 different movie clips (named B1-B5). Each movie clip is placed on a specific x and y. Each movie clip grows/shrinks on roll over/roll out....
I got the code working by typing everything out and duplicating each section per time but it's messy and I'd like to clean up the code by getting a loop to do it (if it's possible?).
This is the code that works but I'd have to duplicate it per movie clip (changing the obvious bits)...
var scene1:MovieClip = new B1();
addChild(scene1);
scene1.x = 170.30;
scene1.y = 231.15;
scene1.addEventListener(MouseEvent.MOUSE_OVER, onRollOverEvent1);
scene1.addEventListener(MouseEvent.MOUSE_OUT, onRollOutEvent1);
function onRollOverEvent1(e:MouseEvent) {
scene1.width=25.9;
scene1.height=25;
}
function onRollOutEvent1(e:MouseEvent) {
scene1.width = 20.9;
scene1.height = 20;
}
Below is what I've tried out but have been stuck for a good while...
for (var i:int=1; i<5; i++){
var scene[i]:MovieClip = new "B"+i();
addChild("scene"+i);
//var scene[i]:MovieClip = new B[i]();
scene[i].addEventListener(MouseEvent.MOUSE_OVER, onRollOverEvent);
scene[i].addEventListener(MouseEvent.MOUSE_OUT, onRollOutEvent)
function onRollOverEvent(e:MouseEvent) {
scene[i].width=25.9;
scene[i].height=25;
}
function onRollOutEvent(e:MouseEvent) {
scene[i].width = 20.9;
scene[i].height = 20;
}
}
scene1.x = 170.30;
scene1.y = 231.15;
scene2.x = 284.30;
scene2.y = 250.75;
scene3.x = 377.30;
scene3.y = 280.15;
scene4.x = 444.30;
scene4.y = 321.15;
scene5.x = 196.30;
scene5.y = 172.15;
First, lets go through your mistakes.
new "B"+i();
At the very best that translates to calling a number i as function and adding the result to "B" as a String. But even new "B1"() is not the same as new B1(). There is, in fact, a method getDefinitionByName(..) that allows to address a class via its name, but I don't recommend to use it because it is advanced topic.
var scene[i]:MovieClip
You just cannot define variables scene1, scene2, etc this way. The closest thing you can actually devise is the square bracket notation: this["scene" + i] = ....
addChild("scene"+i);
The argument must be a DisplayObject instance, not a String.
for (...)
{
...
function onRollOverEvent(e:MouseEvent)
...
}
Do not define functions inside other functions or loops.
scene[i].width = 20.9;
scene[i].height = 20;
By the end of your loop i will be equal to 5, so, what do you think such a record will address?
Then, the solution.
When you come to scaling your working solution to multiple instances, you are to go algorithmic. Loops and Arrays are your friends.
// Lets devise a list of classes and (x,y) coordinates.
var Designs:Array = [
null, // the 0-th element
{id:B1, x:170, y:230},
{id:B2, x:285, y:250},
];
for (var i:int = 1; i < Design.length; i++)
{
// Retrieve a record for the future object.
var aDesign:Object = Designs[i];
// Get a reference to the object's class.
var aClass:Class = aDesign.id;
// Create the object. Yes, you CAN omit () with
// the "new" operator if there are no mandatory arguments.
var aThing:Movieclip = new aClass;
// Set coordinates from the design record.
aThing.x = aDesign.x;
aThing.y = aDesign.y;
// Add to the display list.
addChild(aThing);
// Subscribe the event handlers.
aThing.addEventListener(MouseEvent.MOUSE_OVER, onOver);
aThing.addEventListener(MouseEvent.MOUSE_OUT, onOut);
// Save the object's reference for the later use.
// If you'd need to address, say, 3rd object,
// you do it as following:
// Designs[3].instance
aDesign.instance = aThing;
}
function onOver(e:MouseEvent):void
{
// You subscribed all of the objects to this one event handler.
// This is the correct way to learn, which one of the objects
// is under the mouse and is dispatching the said event.
var aThing:MovieClip = e.currentTarget as MovieClip;
// Change the object's size.
aThing.width = 26;
aThing.height = 25;
}
function onOut(e:MouseEvent):void
{
// Get the source of the dispatched event.
var aThing:MovieClip = e.currentTarget as MovieClip;
// Change the object's size.
aThing.width = 21;
aThing.height = 20;
}
I'm trying to use Rust-Websocket to create a simple chatroom where multiple people can talk to each other.
I looked at the examples and the 'server.rs' and 'websockets.html' looked like a decent starting point to me. So I just tried starting it up and connecting from web. Everything works but I can only communicate with myself and not with other connections (since it sends the message back directly to sender and not to every connection).
So I'm trying to get a vector with all senders/clients so I can just iterate through them and send the message to each one but this seems to be problematic. I cannot communicate the sender or client since It's not thread safe and I cannot copy any of these either.
I'm not sure if I just don't understand the whole borrowing 100% or if it's not intended to do cross-connection communication like this.
server.rs:
https://github.com/cyderize/rust-websocket/blob/master/examples/server.rs
websockets.html:
https://github.com/cyderize/rust-websocket/blob/master/examples/websockets.html
I might be approaching this from the wrong direction. It might be easier to share a received message with all other threads. I thought about this a little bit but the only thing I can think of is sending a message from inside a thread to outside using channels. Is there any way to broadcast messages directly between the threads? All I would need to do is send a string from one thread to the other.
So this is not quite as straight-forward as one might think.
Basically I used a dispatcher thread that would act like a control center for all the connected clients. So whenever a client receives a message it's sent to the dispatcher and this then distributes the message to every connected client.
I also had to receive the messages in another thread because there is no non-blocking way to receive messages in rust-websocket. Then I ways able to just use a permanent loop that checks both for new messages received from the websocket and from the dispatcher.
Here's how my code looked like in the end:
extern crate websocket;
use std::str;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::thread;
use websocket::{Server, Message, Sender, Receiver};
use websocket::header::WebSocketProtocol;
use websocket::message::Type;
fn main() {
let server = Server::bind("0.0.0.0:2794").unwrap();
let (dispatcher_tx, dispatcher_rx) = mpsc::channel::<String>();
let client_senders: Arc<Mutex<Vec<mpsc::Sender<String>>>> = Arc::new(Mutex::new(vec![]));
// dispatcher thread
{
let client_senders = client_senders.clone();
thread::spawn(move || {
while let Ok(msg) = dispatcher_rx.recv() {
for sender in client_senders.lock().unwrap().iter() {
sender.send(msg.clone()).unwrap();
}
}
});
}
// client threads
for connection in server {
let dispatcher = dispatcher_tx.clone();
let (client_tx, client_rx) = mpsc::channel();
client_senders.lock().unwrap().push(client_tx);
// Spawn a new thread for each connection.
thread::spawn(move || {
let request = connection.unwrap().read_request().unwrap(); // Get the request
let headers = request.headers.clone(); // Keep the headers so we can check them
request.validate().unwrap(); // Validate the request
let mut response = request.accept(); // Form a response
if let Some(&WebSocketProtocol(ref protocols)) = headers.get() {
if protocols.contains(&("rust-websocket".to_string())) {
// We have a protocol we want to use
response.headers.set(WebSocketProtocol(vec!["rust-websocket".to_string()]));
}
}
let mut client = response.send().unwrap(); // Send the response
let ip = client.get_mut_sender()
.get_mut()
.peer_addr()
.unwrap();
println!("Connection from {}", ip);
let message: Message = Message::text("SERVER: Connected.".to_string());
client.send_message(&message).unwrap();
let (mut sender, mut receiver) = client.split();
let(tx, rx) = mpsc::channel::<Message>();
thread::spawn(move || {
for message in receiver.incoming_messages() {
tx.send(message.unwrap()).unwrap();
}
});
loop {
if let Ok(message) = rx.try_recv() {
match message.opcode {
Type::Close => {
let message = Message::close();
sender.send_message(&message).unwrap();
println!("Client {} disconnected", ip);
return;
},
Type::Ping => {
let message = Message::pong(message.payload);
sender.send_message(&message).unwrap();
},
_ => {
let payload_bytes = &message.payload;
let payload_string = match str::from_utf8(payload_bytes) {
Ok(v) => v,
Err(e) => panic!("Invalid UTF-8 sequence: {}", e),
};
let msg_string = format!("MESSAGE: {}: ", payload_string);
dispatcher.send(msg_string).unwrap();
}
}
}
if let Ok(message) = client_rx.try_recv() {
let message: Message = Message::text(message);
sender.send_message(&message).unwrap();
}
}
});
}
}
http://pastebin.com/H9McWLrH
I am trying to create an app on pebble using MenuLayer. My intention is to create Menu's and then sub-menus using the MenuLayer.
I did some basic implementation using "menu_layer_set_callbacks" method and registering callbacks methods for each submenu layers. However, now I realise its getting too messy.
I want to know if there is any easy way out provided in the framework to meet this requirement.
Thanks.
KK
I don't know what you ended up with, but for my project, I made a set of sub-windows but reuses the same callbacks, you can pass custom data to let each know what type it is. Then just push the required sub-windows as needed
void sub_window_load(Window* parentWindow) {
Layer* window_layer = window_get_root_layer(parentWindow);
const GRect bounds = layer_get_frame(window_layer);
MenuLayer* new_layer = menu_layer_create(bounds);
const int context = *((int*) window_get_user_data(parentWindow));
if (context == ID_A) s_A_layer = new_layer;
else if (context == ID_B) s_B_layer = new_layer;
menu_layer_set_callbacks(new_layer, window_get_user_data(parentWindow), (MenuLayerCallbacks){
.get_num_sections = sub_menu_get_num_sections_callback,
.get_num_rows = sub_menu_get_num_rows_callback
/* etc */
});
menu_layer_set_click_config_onto_window(new_layer, parentWindow);
layer_add_child(window_layer, menu_layer_get_layer(new_layer));
}
void createSubWin(Window** w, int* context) {
*w = window_create();
window_set_user_data(*w, context);
window_set_window_handlers(*w, (WindowHandlers) {
.load = sub_window_load,
.unload = sub_window_unload
});
}
I keep hearing that using immutable data structures and immutable objects is a good pattern for thread safety and preventing race conditions without needing to use semaphores, but I still can't think of a way to use them. Even for the most simple scenarios. For example
int a = 0;
Semaphore s = new Semaphore();
void thread1() {
s.wait();
if (a == 2) {
// do something
}
a = 1;
s.signal();
}
void thread2() {
s.wait();
if (a == 1) {
// do something
}
a = 2;
s.signal();
}
How can I change this code to use an immutable object for a?