How can I configure an action that creates a variable number of objects? - palantir-foundry

I am looking to create an action type that can be used to create a variable number of objects of a given object type. In other words, a user should be able to use this action to create 3 objects at once, 5 objects at once, etc.
I tried to accomplish this using the action configuration UI in OMA, but ran into the following issues:
I noticed that in the “Rules” section, it is only possible to define a static number of objects to be created. In the example shown in the screenshot below, you’d only be able to create 2 objects rather than a variable number.
Similarly, there is no way to specify a variable number of parameters in the “Form” section, which would be necessary to capture the primary keys for each object the user would like to create. I thought of specifying a string parameter that takes in multiple values as an alternative, but that wouldn’t work because there is no way to assign a single value from this parameter to an object property.
How should I go about accomplishing this?

It would be possible to create a variable number of objects using a function backed action! In particular, you could take the following steps:
Create a new function that takes in a list of primary keys as input and creates an object for each primary key in the list. The code for this function could look something like this:
#Edits(ObjectA)
#OntologyEditFunction()
public createMultipleObjects(primaryKeys: string[]): void {
// Loop through pkeys and create a new object for each pkey
primaryKeys.forEach(k => {
Objects.create().objectA(k)
});
}
You can also reference the following documentation for more guidance on how to define Ontology edit functions.
Create an action in OMA that calls the function that you defined in step 1. You will need to define a multi-value string parameter for this action, which will be passed as an input to the function.
You can refer to the following documentation (https://www.palantir.com/docs/foundry/action-types/function-actions-getting-started/) for a step by step guide on how to configure a function backed action.

Related

Is it possible to set an Actions submission criteria that prevents submission if an object has more than a certain number of linked objects?

I currently have an action that creates a new link between an object of type A, named OA, and an object of type B, named OB.
Our workflow has a constraint such that any object of type B can at most, be linked to 4 objects of type A. As such, I would like to define a submission criterion in the action such that submission is blocked if OB is already linked to 4 objects of type A.
I couldn't find a straightforward way to do this using the Action configuration UI. How could I accomplish this?
The easiest way to accomplish this would be to turn your action into a function backed action. This would allow you to take the following steps to accomplish the desired functionality:
You can search around to all objects of type A that are linked to OB by writing something like:
// Search around to all objects of type A that are linked to OB
const linkedObjects = OB.objectTypeA.all();
// Now get the number of linked objects
const numLinkedObjects = linkedObjects.length;
Prevent the function from running by throwing a UserFacingError if there are more than 4 linked objects
if (numLinkedObjects >= 4) {
throw new UserFacingError("Objects of type B cannot be linked to more than 4
objects of type A");
}
For reference, here are some relevant pages in Foundry’s documentation:
Creating Function Backed Actions (https://www.palantir.com/docs/foundry/action-types/function-actions-getting-started/#getting-started)
Accessing link types in Functions (https://www.palantir.com/docs/foundry/functions/api-objects-links/#link-types)
Throwing UserFacingErrors from Functions (https://www.palantir.com/docs/foundry/functions/user-facing-error/)
While you can certainly do this in a Function-backed action, the tradeoff is that you won't get up front validation of the criteria, but rather the user will submit the action and then will received a toast showing the UserFacingError text. So while this technically achieves the validation, it is a sub-par user experience compared to disabling the button with a message or otherwise catching the condition upstream of the action itself in the workflow.
An alternative, iff you're using the action exclusively through Workshop, (this won't work if you want the action to "stand alone" in object explorer), you can create an object set variable that holds the result of the search around and pass that in as a hidden parameter to the Action. You can then set up the Action submission criteria as normal to check the length of that parameter and provide a message back to the user. You can also use that information in the app itself to, for example, conditionally show or hide some other workflow for the condition.
If you take this approach, make sure to add the hubble-oe:hide-action typeclass to one of the object parameters in the Action Form configuration so that the Action doesn't show up where users could use it through Object Explorer.

Yii2: Proper Structuring of Actions based on User Roles

I'm quite worried with the current way I structure actions in my controllers.
I'm not sure which is the more adopted method for implementing actions that show different things for different users based on their type.
For example:
Creating a Model when User is Type 1 uses the same action but passes more parameters to the view than User Type 2.
Creating a Model when User is Type 2 uses same action but passes less parameters to the view and hence there are if statements in the view to show/hide fields based on the User Type.
Is this a proper way of doing things? If not, can you direct me to some documentation that explains a good structure?
Thanks & appreciate your help.
A simple but trival way is this
you can pass an array (eg $param) and then evaluate the type for do the right thinghs inside your action
public function actionYourAction( $param)
{
$type = $param['type'];
switch($param['type']){
case 'TYPE1' :
....
break;
}
a more clean solution could be a proper object oriented class method specialization for user object, instantiate the proper user object where you nedd and pass thsi in action call. Inside the actione simply use the object (specilized) method .

AS3: how to pass by "object"

I was actually looking for a way to pass by reference in AS3 but then it seemed that adobe and lots of people's understanding of pass by reference is different from what I have been taught at the university. I was taught java was pass by value and C++ allowed pass by reference.
I'm not trying to argue what pass by value and reference are. I just want to explain why I'm using pass by object in the question...
Back to the question, I would like to do something like:
public function swapCard(cardA:Cards, cardB:Cards) {
var temp:Cards = cardA;
cardA = cardB;
cardB = temp;
}
...
swapCard(c1, c2);
EDIT: adding two examples on how I'm using the swapCard function
1) in the process of swaping a card between player1 and player2's hand
swapCard(player1.hand[s], player2.hand[t]);
2) in the process of swaping a card between player1's hand and deck
swapCard(player1.hand[s], player1.deck[rand]);
In C++, we only need to add a symbol before the parameters to make it work (and we call THIS pass by reference). But in AS3, cardA and cardB are just pointers to the formal parameters. Here in the function, changing the pointers does not do anything to the formal parameters :(
I have been searching for hours but I couldn't find a way to without knowing all the properties of the Cards.
If I have to change the properties of the cards one by one then maybe I should change swapCard to a static function in class Cards? (because I don't want to expose everything to another class) I'm not sure if this is a good practice either. This is like adding a swap_cars function into class Cars. If I let this happen, what will be next? Wash car, lend car, rent car... I really want to keep the Cards class clean and holds only the details of the card. Is there a possible way to do this properly in AS3?
The kind of swap function that you're trying to implement is not possible in AS3. The input parameters are references to the input objects but the references themselves are passed by value. This means that inside the function you can change the cardA and cardB but those changes will not be visible outside the function.
Edit: I added this portion after you edited your question with sample usage.
It seems like you're trying to swap two objects in 2 different arrays at given array positions in each - you can create a function for this in AS3 but not the way you attempted.
One possible implementation is to pass the arrays themselves and the positions that you're trying to exchange; something like this:
// Assumes arrays and indices are correct.
public function SwapCards(playerHand:Array, playerCardIndex:int,
playerDeck:Array, playerDeckIndex:int):void
{
var tempCard:Card = playerHand[playerHandIndex];
playerHand[playerHandIndex] = playerDeck[playerDeckIndex];
playerDeck[playerDeckIndex] = tempCard;
}
Note that you still exchange references and the arrays themselves are still passed by reference (and the array references are passed by value - you could, if you wanted, change the arrays to new arrays inside this function but you wouldn't see new arrays outside). However, because the array parameters refer to the same arrays inside and outside the function, you can make changes to the contents of the array (or other array properties) and those changes will be visible outside.
This solution is faster than cloning the card because that involves allocating memory for a new Card instance (which is expensive) and that temporary instance will also have to be freed by the garbage collector (which is also expensive).
You mentioned in a comment that you pass cards down to lower levels of code - if you don't have a back reference to the arrays (and the positions of the cards), you will not be able to easily swap cards - in AS3, all input parameters are copies (either the copy of the value for primitive types or the copy of the reference for complex objects - changes to the input parameters in a function will not be visible outside).
EDIT: renaming the function from clone to copyFrom as pointed out by aaron. Seems like clone is supposed to be used as objA = objB.clone()
At this point, I'm adding a copyFrom() function in the Cards class such that
var temp:Cards = new Cards(...);
var a:Cards = new Cards(...);
...
temp.copyFrom(a);
...
temp will be copying everything from a.
public function swapCard(cardA:Cards, cardB:Cards) {
var temp:Cards = new Cards();
temp.copyFrom(cardA);
cardA.copyFrom(cardB);
cardB.copyFrom(temp);
}
I will wait for a week or so to see if there are any other options
You have some good answers already, but based on the comments back-and-forth with me, here's my suggestion (I use "left" and "right" naming because it helps me visualize, but it doesn't matter):
function swapCard(leftCards:Array, leftCard:Card, rightCards:Array, rightCard:Card):void {
var leftIndex:int = leftCards.indexOf(leftCard);
var rightIndex:int = rightCards.indexOf(rightCard);
leftCards[leftIndex] = rightCard;
rightCards[rightIndex] = leftCard;
}
Now you can swap the cards in the two examples you posted like this:
swapCard(player1.hand, player1.hand[s], player2.hand, player2.hand[t]);
swapCard(player1.hand, player1.hand[s], player1.deck, player1.deck[rand]);
However, note that while this swaps the cards in the arrays, it does not swap direct references to the cards in those arrays. In other words:
var a:Card = player1.hand[0];
var b:Card = player2.hand[0];
swapCard(player1.hand, a, player2.hand, b);
// does not change the references a and b, they still refer to the same card
a == player2.hand[0];
a != player1.hand[0];
b == player1.hand[0];
b != player2.hand[0];
Typically, this sort of thing is handled by dispatching a "changed" event so that any code that cares about the state of a player's hand array will know to re-evaluate the state of the hand.
There's a deep misunderstanding going on here. The question is about object reference but the PO is not trying to swap any Object reference at all.
The problem comes from the fact that the PO does not understand the difference between variable and objects. He's trying to swap variable/object reference which is not dynamically possible of course. He wants with a function to make the variable holding a reference to Object A, swap its object reference with another variable. Since Objects can be passed around but not variables (since they are just holders (not pointers)) the task is not possible without a direct use of the given variable.
To resume:
variables are not Objects!
variables hold a reference to an object.
variables cannot be passed in function or referenced in functions because THEY ARE NOT OBJECTS.

Access functions and variables from another script in Unity

I've been searching for this for few hours now and didn't get any result. I have a script called GameSetup.js that is attached to an empty game object (named GM) . It holds references to my current camera and has functions to get screen width and other stuff. I'd like to access these functions ( and variables) from other scripts. so for example I have another empty game object called "Blocks", one of its components is BlockGenerator.js , which has BlockGenerator() function. I want to be able to use screenWidth() in BlockGenerator() like this:
BlockGenerator.js
BlockGenerator(){
var Blocknumber : int = GameSetup.ScreenWidth / blockWidth;
}
GameSetup.js
function ScreenWidth() : float{
var screenWidth : float = (mainCam.ScreenToWorldPoint(new Vector3(Screen.width,0f, 0f)).x)* 2;
return screenWidth;
of course GameSetup is not recognized in BlockGenerator.js if I use GetComponent(), for example. I'm not sure if I can drag GM game object in the inspector and pass it to BlockGenerator.js , I haven't tried. but the thing is, it makes the game script very complicated for no good reason. how can I setup and access global functions and variables?
In the class that holds the BlockGenerator method you need to define a public variable of type GameSetup. Then in the inspector drag the object whose instance variables you wish to access. After that you can access the object in your BlockGenerator method and call its methods and access its instance variables.
Another way is to find the object by tag in BlockGenerator via https://docs.unity3d.com/Documentation/ScriptReference/GameObject.FindGameObjectsWithTag.html or search by type with http://docs.unity3d.com/Documentation/ScriptReference/Object.FindObjectOfType.html.

How to Map Data Types to Custom Types?

Consider the following function:
public function foo(bar1:int, bar2:uint, bar3:String, bar4:Boolean):void{}
What I want is to have the different types of data represented by custom named types which are essentially representing the original data types. In other words, I would like to proxy the data types and have a valid function as following:
public function foo(bar1:PAR_Bar1, bar2:PAR_Bar2, bar3:PAR_Bar3, bar4:PAR_Bar4):void{}
so PAR_Bar1 would proxy the int data type, PAR_Bar2 would proxy the uint data type, so on and so forth.
The reason I need this is because I'm using a debugger with a GUI that can run methods and allows changing function parameter values in real-time, the issue is that the debugger can't tell me what parameter I'm changing, it only displays the data type of a parameter. So if I need to change 10 different parameters all of type int, the debuggers display all of them as int and not by their names.
I think that if I use proxy types I can easily differentiate between parameters.
So, my question: Is it possible to proxy data types? I mean map specific data types to custom data types that would represent the base data types?
EDIT: I'm using the Monster Debugger and this is the window of a method called in real-time:
As you can see, I don't get the parameters' names but their type (int).
I would recommend you changed your debugger but since this is a proper question...
You can create a class just like any constant:
const PAR_Bar1:Class = uint;
Let's hope your debugger will identify this class and not its mother.
Not exactly sure what you are going to use this for but have you considered using an untyped variable definition?
public function foo(bar1:*, bar2:*, bar3:*, bar4:*):void{}
Then using this to get the class of the variables?
var PAR_Bar1:Class = Object(bar1).constructor;
EDIT: Ah ignore this one, re-read your question and realised this won't help you.
It seems that there are no ways of aliasing types.