-
Notifications
You must be signed in to change notification settings - Fork 0
/
site.json
1 lines (1 loc) · 34.2 KB
/
site.json
1
{"title":"Atom.apex","githubRepo":"Click-to-Cloud/Atom.apex","googleAnalytics":"","index":{"title":"Home","description":"Atom.apex is a library that tries to save you from the doom of Salesforce governor limits.","content":" Multi-step Execution Atom.apex splits your business logic into steps, which can be executed in multiple queueable jobs. Functional Support Atom.apex can be integrated easily with functions. ","srcFilePath":"src/pages/index.soy","id":"pages","location":"/./","url":"/atom-apex/./","children":{"docs":{"title":"Docs","description":"Everything you need to know to get started.","content":" Docs Start learning how to leverage the power of . Choose a Guide Each one provide step by step coverage for every core feature. ","srcFilePath":"src/pages/docs/index.soy","id":"docs","location":"/docs/","url":"/atom-apex/docs/","children":{"search":{"title":"Search","description":"Find what you're looking for in the documentation.","hidden":true,"content":" Electric Docs Start learning how to leverage the power of . ","srcFilePath":"src/pages/docs/search.soy","id":"search","location":"/docs/search.html","url":"/atom-apex/docs/search.html"},"Atom":{"children":{"compute":{"title":"Compute","description":"Compute","layout":"guide","icon":"flash","weight":2,"content":" {$page.description} What is a Compute? A compute is the basic unit of you business logic, the building blocks. Most of the time, you will group your code into computes and then chain them into Atoms. A compute should be planned carefully so that it runs within the governor limits. To make it simple, double check that no loops are used with a possible large dataset. Custom Compute You can create a custom compute. public class CustomCompute extends Atom.Compute { public override void execute(Atom.State s) { Integer count = (Integer)s.getData('count'); count += 1; s.setData('count', count); } } Bear in mind that computes run in queueable jobs, and they should interact only with Atom states. Func Compute Atom.apex offers FuncCompute to help wrap functions into computes. Here is an example: new Atom() .then(new Atom.FuncCompute(R.debug.apply('test'))) .fork(); In fact, this is usually simplified into this: new Atom() .then(R.debug.apply('test')) .fork(); ","srcFilePath":"src/pages/docs/Atom/compute.md","id":"compute","location":"/docs/Atom/compute.html","url":"/atom-apex/docs/Atom/compute.html"}},"title":"Atom","description":"Atom","layout":"guide","icon":"flash","weight":1,"content":" {$page.description} What is an Atom? An atom is originally an unsplittable unit. In Atom.apex, we refer it as a piece of business logic that gets executed even when governor limits are reached. Usually when we reach the governor limits(heap size, cpu time, query, ...), it is because we are repeating a resource-consuming action. Everything works when there is reasonable repetition. But the unexpected data may cause the repetition count to grow out of control. By then, the governor limits are reached, exceptions are thrown, business logic is aborted and you are left in a mess(especially when you interact with external services). What we have dreamed is that if the governor limits are reached, Salesforce does not throw exceptions directly, and instead it will schedule our remaining logic to the next available time spot to continue. Sadly this has always been a dream and Salesforce aborts your execution brutally in this case(roll back your transaction as well, which is a good thing). This is how we got our inspiration. We want to split our business logic into steps, so that each step can be executed sequentially and if resources are running out, we have a chance to save our progress and then continue the remaining steps. We call the whole process an Atom, which means that your business logic is guaranteed to be executed even if some governor limits are reached, so that it will not be broken. How is Atom implemented? In essence, an Atom is just a chain of queueable jobs in Apex. We split an Atom instance into steps, and each step is executed one after another. Every time a step is executed, Atom.apex will check if the governor limits are reached. If so, Atom.apex will stop executing, save the current data and start a new queueable job to continue the remaining code. How should Atom be used? Atom.apex should be used when there is the need to handle scalable code execution that may break the governor limits. So it should be used at the entry point of the execution context. Also it uses chained queueable jobs to execute the code, which means that it cannot be used in a synchronous context. Here is the outline of the usage. Map initialData = new Map{ ... }; new Atom(initialData) .then(new CustomBeforeCompute()) .then(new Atom.ForEachStep('item', 'items', new CustomCompute())) .then(new CustomAfterCompute()) .fork(); Constructors | Constructor | Description | | ------ | ----------- | | Atom() | Create an empty Atom | | Atom(Map<String, Object>) | Create an Atom with initial data | Methods | Method | Description | | ------ | ----------- | | Atom.State getState() | Get the Atom state | | Integer getMaxInterruptions() | Get the max interruptions | | Atom setMaxInterruptions(Integer) | Set the max interruptions | | Atom then(Atom.Step) | Chain the next step | | Atom then(Atom.Compute) | Chain the next compute in a step | | Atom then(Func f) | Chain the next Func in a step | | Atom fork() | Execute the Atom | ","srcFilePath":"src/pages/docs/Atom/index.md","id":"Atom","location":"/docs/Atom/","url":"/atom-apex/docs/Atom/","childIds":["compute"]},"Monitor":{"title":"Monitor","description":"Monitor","layout":"guide","icon":"cloud","weight":4,"content":" {$page.description} Atom Monitors When the governor limits are reached is determined by monitors. Atom.apex has the following built-in monitors. | Name | Description | | ---- | ----------- | | AggregateQueriesMonitor | Aggregate query limit | | CalloutsMonitor | Call out limit | | CpuTimeMonitor | Cpu time limit | | DMLRowsMonitor | DML rows limit | | DMLStatementsMonitor | DML statement limit | | EmailInvocationsMonitor | Email invocation limit | | FutureCallsMonitor | Future call limit | | HeapSizeMonitor | Heap size limit | | MobilePushApexCallsMonitor | Mobile push apex call limit | | QueriesMonitor | Query limit | | QueryLocatorRowsMonitor | Query locator row limit | | QueryRowsMonitor | Query row limit | | QueueableJobsMonitor | Queueable job limit | | SoslQueriesMonitor | SOSL query limit | Most of the time, you don't need to care about monitors. However, you could still provide your own monitor. public class CustomMonitor extends Atom.DefaultMonitor { public CustomMonitor() { super('Custom limit is reached'); } public override Integer getCurrentValue(Atom.State s) { return (Integer)s.getData('count'); } public override Integer getMaxValue(Atom.State s) { return 1000; } } Atom.registerMonitor(new CustomMonitor()); ","srcFilePath":"src/pages/docs/Monitor/index.md","id":"Monitor","location":"/docs/Monitor/","url":"/atom-apex/docs/Monitor/","children":{"methods":{"title":"Methods","description":"methods of R","layout":"guide","icon":"cloud","weight":3,"content":" {$page.description} adjust Update the element at index with the function. R.with(1, 2, 3).adjust(R.add.apply(1), 1).debug(); // (1, 3, 3) all Check if all elements match the predicate. R.with(1, 2, 3).all(R.equals.apply(1)).debug(); // false append Append the element to the elements. R.with(1, 2, 3).append(4).debug(); // (1, 2, 3, 4) assoc Associate the value to the key. R.withObj('name', 'value').assoc('name', 'newValue').debug(); // {name=newValue} capitalize Return a new string with the first letter in uppercase. R.of('cat').capitalize().debug(); // Cat clamp Return a value that is limited between the min and the max. R.of(4).clamp(1, 3).debug(); // 3 compact Remove null values from the elements. R.with('a', null, 0).compact().debug(); // (a, 0) concat Concatenate the other. R.with(1, 2, 3).concat(R.with(4, 5, 6)).debug(); contains Check if the target is contained. R.with(1, 2, 3).contains(2).debug(); // true containsBy Check if the target is contained by the predicate. R.with(1, 2, 3).containsBy(R.equals, 2).debug(); // true containsKey Check if the key is contained. R.withObj('name', 'test').containsKey('name').debug(); // true countBy Get a result of count mapped by the key. R.with(1, 2, 2).countBy(R.identity).debug(); debug Print debug information. R.with(1, 2).debug(); // (1, 2) defaultTo Get the default value. R.of(null).defaultTo(3).debug(); // 3 difference Do a difference with the other object. R.with(1, 2, 3).difference(R.with(2, 3, 4)).debug(); // (1) dissoc Remove the value mapped by the key. R.withObj('name', 'value').dissoc('name').debug(); // {} doClone Get a clone. R.with(1, 2, 3).doClone().debug(); // (1, 2, 3) doInsert Insert the element at the index. R.with(1, 2, 3).doInsert(1, 'x').debug(); // (1, x, 2, 3) doInsertAll Insert all the elements at the index. R.with(1, 2, 3).doInsertAll(1, R.with('x', 'y')).debug(); // (1, x, y, 2, 3) doJoin Join the elements with the separator. R.with(1, 2, 3).doJoin('-').debug(); // 1-2-3 doMap Map a function over the elements. R.with(1, 2, 3).doMap(R.add.apply(1)).debug(); // (2, 3, 4) doMerge Merge the source. R.withObj('name', 'test').doMerge(R.withObj('name', 'newTest')).debug(); // {name=newTest} doUpdate Update the element at the specified index. R.with(1, 2, 3).doUpdate(1, 3).debug(); // (1, 3, 3) drop Drop the first N elements. R.with(1, 2, 3).drop(2).debug(); // (3) dropRight Drop the first N elements from right. R.with(1, 2, 3).dropRight(2).debug(); // (1) dropRightWhile Drop from right until the predicate is not satisfied. R.with(1, 2, 3).dropRightWhile(R.equals.apply(1)).debug(); // (1, 2, 3) dropWhile Drop until the predicate is not satisfied. R.with(1, 2, 3).dropWhile(R.equals.apply(1)).debug(); // (2, 3) endsWith Check if the elements end with the value. R.of('abc').endsWith('bc').debug(); // true every Check if every element matches the predicate. R.with(1, 2, 3).every(R.equals.apply(1)).debug(); // false evolve Apply the function to the value mapped by the same key and calculate the evolved object. R.withObj('name', 'test').evolve(new Map{ 'name' = R.append.apply('more') }).debug(); // {name=testmore} filter Call the function to filter the elements of instance R. R.with(1, 2, 3).filter(R.equals.apply(2)).debug(); // (2) R.with(new Account(Description = 'desc'), new Account()).filter('Description').debug(); // (Account:{Description=desc}) R.with(new Account(Description = 'desc'), new Account()).filter('Description', 'desc').debug(); // (Account:{Description=desc}) R.with(new Account(Description = 'desc'), new Account()).filter(new Map{ 'Description' = 'desc' }).debug(); // (Account:{Description=desc}) find Find the first element that satisfies the predicate. R.with(1, 2, 3).find(R.equals.apply(2)).debug(); // 2 findIndex Find the index of the first element that matches the predicate. R.with(1, 2, 3).findIndex(R.equals.apply(2)).debug(); // 1 findLast Find the first element that satisfies the predicate from last. R.with(1, 2, 3).findLast(R.equals.apply(2)).debug(); // 2 findLastIndex Find the index of the first element that matches the predicate from last. R.with(1, 2, 3).findLastIndex(R.equals.apply(2)).debug(); // 1 first Get the first element. R.with(1, 2, 3).first().debug(); // 1 flatten Flatten the elements recursively. R.with(1, new List{ 2, new List{ 3 } }, 4).flatten().debug(); // (1, 2, 3, 4) forEach Call the function to each element of instance R. R.with(1, 2, 3).forEach(R.debug); // 1 // 2 // 3 fromPairs Convert from a list of pairs to a map. R.with(new R.Pair('name', 'test')).fromPairs().debug(); // {name=test} groupBy Get a result of count mapped by the key. R.with(1, 2, 2).countBy(R.identity).debug(); head Get the first element. R.with(1, 2, 3).head().debug(); // 1 indexBy Get object indexed by the key. R.with(1, 2, 2).indexBy(R.identity).debug(); indexOf Get the index of the target. R.with(1, 2, 3).indexOf(2).debug(); // 1 init Get the elements except the last. R.with(1, 2, 3).init().debug(); // (1, 2) intersection Do an intersection with the other object. R.with(1, 2, 3).intersection(R.with(2, 3, 4)).debug(); // (2, 3) invert Get an inverted map, values mapped by duplicate keys are put in a list. R.withObj('name', 'test').invert().debug(); invertObj Get an inverted map. R.withObj('name', 'test').invertObj().debug(); // {test=name} isEmpty Check if it is empty. R.with(1, 2, 3).isEmpty().debug(); // false keys Get the keys of the wrapped map. R.withObj('name', 'test').keys().debug(); // {name} last Get the last element. R.with(1, 2, 3).last().debug(); // 3 lastIndexOf Get the index of the target from the last. R.with(1, 2, 3).lastIndexOf(2).debug(); // 1 length Get the length, same as size(). R.with(1, 2, 3).length().debug(); // 3 match Get a list of matched groups after doing a regex match. R.of('abc').match('.(a).').debug(); // (abc, a) none Check if none of the elements matches the predicate. R.with(1, 2, 3).none(R.equals.apply(1)).debug(); // false nth Get the nth element. R.with(1, 2, 3).nth(1).debug(); // 2 omit Omit the values specified by the list of keys. R.of(new Account(FirstName='test', Description='desc')).omit(new List{ 'Description' }).debug(); // {FirstName=test} pad Pad the string to the length. R.of('cat').pad(5, '').debug(); // *cat padLeft Pad the string to the length from the left. R.of('cat').padLeft(5, '').debug(); // *cat padRight Pad the string to the length from the right. R.of('cat').padRight(5, '').debug(); // cat* partition Create a paritioned pair using the predicate. R.with(1, 2, 3).partition(R.equals.apply(1)).debug(); // Pair:[fst=(1), snd=(2, 3)] pick Pick the values specified by the list of keys. R.of(new Account(FirstName='test', Description='desc')).pick(new List{ 'Description' }).debug(); // {Description=desc} pluck Extract the field out of the object to a new list. R.with(new Account(Description='desc')).pluck('Description').debug(); // (desc) prepend Prepend the element to the elements. R.with(1, 2, 3).prepend(4).debug(); // (4, 1, 2, 3) project Project a list of objects using the list of fields. R.with(new Account(Description='desc', FirstName='test')).project(new List{ 'Description' }).debug(); // ({Description=desc}) reduce Reduce over the elements. R.with(1, 2, 3).reduce(R.add, 0).debug(); // 6 reject Reject the elements by checking with the predicate. R.with(new Account(Description = 'desc'), new Account()).reject((Func)R.complement.run(R.has.apply('Description'))).debug(); // (Account:{Description=desc}) R.with(new Account(Description = 'desc'), new Account()).reject('Description').debug(); // (Account:{}) R.with(new Account(Description = 'desc'), new Account()).reject('Description', 'desc').debug(); // (Account:{}) R.with(new Account(Description = 'desc'), new Account()).reject(new Map{ 'Description' = 'desc' }).debug(); // (Account:{}) remove Remove elements starting at the index and specified by the count. R.with(1, 2, 3).remove(1, 3).debug(); // (1) repeat Create a list of elements by repeating the element. R.of('a').repeat(3).debug(); // (a, a, a) replace Replace the string according to the pattern and replacement. R.of('I love cats').replace('cat', 'dog').debug(); // I love dogs replaceAll Replace all the strings according to the pattern and replacement. R.of('I love cats').replaceAll('cat', 'dog').debug(); // I love dogs reverse Reverse the elements. R.with(1, 2, 3).reverse().debug(); // (3, 2, 1) sample Get a random element. R.with(1, 2, 3).sample().debug(); // 3 sampleSize Get a list of random elements. R.with(1, 2, 3).sampleSize(2).debug(); // (3, 1) shuffle Create a shuffled list of elements. R.with(1, 2, 3).shuffle().debug(); // (3, 1, 2) size Get the size, same as length(). R.with(1, 2, 3).size().debug(); // 3 slice Get a slice of the elements. R.with(1, 2, 3).slice(1, 2).debug(); // (2) some Check if some elements match the predicate. R.with(1, 2, 3).some(R.equals.apply(1)).debug(); // true sortBy Sort the elements by the comparator. R.with(new Account(Description='abc'), new Account(Description='def')).sortBy((Func)R.descend.run(R.prop.apply('Description'))).debug(); // (Account:{Description=def}, Account:{Description=abc}) sortDefault Sort the elements by default. R.with(3, 2, 1).sortDefault().debug(); // (1, 2, 3) split Split the string by the separator. R.of('a/b/c').split('/').debug(); // (a, b, c) startsWith Check if the elements start with the value. R.with(1, 2, 3).startsWith(new List{ 1, 2 }).debug(); // true sum Get the sum. R.with(1, 2, 3).sum().debug(); // 6 tail Get the elements except the first. R.with(1, 2, 3).tail().debug(); // (2, 3) take Take the first N elements. R.with(1, 2, 3).take(2).debug(); // (1, 2) takeRight Take the first N elements from right. R.with(1, 2, 3).takeRight(2).debug(); // (2, 3) takeRightWhile Take elements from right until the predicate is not satisfied. R.with(1, 2, 3).takeRightWhile(R.equals.apply(1)).debug(); // () takeWhile Take elements until the predicate is not satisfied. R.with(1, 2, 3).takeWhile(R.equals.apply(1)).debug(); // (1) test Test the string according to the pattern. R.of('cat').test('.b.').debug(); // false toLower Convert the string to lowercase. R.of('Cat').toLower().debug(); // cat toPairs Convert a map to a list of pairs. R.withObj('name', 'test').toPairs().debug(); // (Pair:[fst=name, snd=test]) toUpper Convert the string to uppercase. R.of('cat').toUpper().debug(); // CAT transform Transform the wrapped value using the function. R.with(1, 2, 3).transform(R.size).debug(); // 3 trim Trim the string. R.of(' a ').trim().debug(); // a union Do a union with the other object. R.with(1, 2, 3).union(R.with(2, 3, 4)).debug(); // (1, 2, 3, 4) uniq Return unique elements. R.with(1, 2, 2).uniq().debug(); // (1, 2) unnest Flatten the elements by one level. R.with(1, new List{ 2, 3 }, 4).unnest().debug(); // (1, 2, 3, 4) values Get the values of the wrapped map. R.withObj('name', 'test').values().debug(); // (test) without Same as difference. R.with(1, 2, 3).without(R.with(2, 3, 4)).debug(); // (1) xor Do an xor with the other object. R.with(1, 2, 3).xor(R.with(2, 3, 4)).debug(); // (1, 4) zip Create a zipped list of pairs according to the mList. R.with('a', 'b').zip(new List{ 1, 2 }).debug(); // (Pair:[fst=1, snd=a], Pair:[fst=2, snd=b]) zipObj Create a zipped map according to the mList. R.with('a', 'b').zipObj(new List{ 1, 2 }).debug(); // {1=a, 2=b} ","srcFilePath":"src/pages/docs/Monitor/methods.md","id":"methods","location":"/docs/Monitor/methods.html","url":"/atom-apex/docs/Monitor/methods.html"}},"childIds":["methods"]},"Step":{"children":{"function":{"title":"Function","description":"Function","layout":"guide","icon":"code-file","weight":3,"content":" {$page.description} Atom Function Atom.apex provides helper functions to wrap existing functions. compute Compute the given Func with arguments from the state and set the value back Func f = Atom.F.compute.apply('count', R.inc, 'count'); // Get the 'count', increment it and set the value back to 'count' Func f = Atom.F.compute.apply(R.inc, 'count'); // Get the 'count', increment it and do not set it back ","srcFilePath":"src/pages/docs/Step/function.md","id":"function","location":"/docs/Step/function.html","url":"/atom-apex/docs/Step/function.html"},"state":{"title":"State","description":"State","layout":"guide","icon":"code-file","weight":2,"content":" {$page.description} State An Atom holds a state, which is then shared by all of its steps. You can access data with the state. public class CustomCompute extends Atom.Compute { public override void execute(Atom.State s) { Integer count = (Integer)s.getData('count'); count += 1; s.setData('count', count); } } Reference | Method | Description | | ------ | ----------- | | Map<String, Object> all() | Get all the data | | Object getData(String) | Get the data by the key | | void setData(String, Object) | Set the data by the key and value | | Boolean isInterrupted() | Check if it is interrupted | | void setInterrupted(Boolean) | Set whether it is interrupted | | Integer getInterruptedTimes() | Get the interrupted times | ","srcFilePath":"src/pages/docs/Step/state.md","id":"state","location":"/docs/Step/state.html","url":"/atom-apex/docs/Step/state.html"}},"title":"Step","description":"Step","layout":"guide","icon":"code-file","weight":2,"content":" {$page.description} What is a Step? A step is the basic component of an Atom. Atom.apex supports many types of steps. Simple Step Composite Step ForEach Step Range Step Repeat Step Simple Step A simple step is one that contains a compute. The only job for a simple step is to execute that compute. new Atom() .then(new Atom.SimpleStep(new CustomCompute())) .then(new CustomCompute()) // equivalent to above .fork(); | Constructor | Description | | ----------- | ----------- | | SimpleStep(Atom.Compute) | Create a simple step from the compute | Composite Step A composite step is one that can contain multiple steps. It executes the children steps one by one. new Atom() .then( new Atom.CompositeStep() .then(...) ) .fork(); | Constructor | Description | | ----------- | ----------- | | CompositeStep() | Create an empty composite step | | Method | Description | | ------ | ----------- | | CompositeStep then(Atom.Step) | Chain the next step | | CompositeStep then(Atom.Compute) | Chain the next compute as the step | | CompositeStep then(Func) | Chain the next Func as the step | ForEach Step A ForEachStep is one that executes the 'for-each' loop with the given step. It looks up for the collection data with the given name from the Atom state, and creates a new looping item before invoking the looping step. Map initialData = new Map{ 'items' = new List{ ... } }; new Atom(initialData) .then(new Atom.ForEachStep('item', 'items', new CustomStep())) .fork(); | Constructor | Description | | ----------- | ----------- | | ForEachStep(String, String, List<Object>, Atom.Step) | Create a ForEachStep with item key, values key, values and the next step | | ForEachStep(String, String, Atom.Step) | Create a ForEachStep with item key, values key and the next step | | ForEachStep(String, List<Object>, Atom.Step) | Create a ForEachStep with item key, values and the next step | | ForEachStep(String, String, Atom.Compute) | Create a ForEachStep with item key, values key and compute as the next step | | ForEachStep(String, List<Object>, Atom.Compute) | Create a ForEachStep with item key, values and compute as the next step | | ForEachStep(String, String, Func) | Create a ForEachStep with item key, values key and Func as the next step | | ForEachStep(String, List<Object>, Func) | Create a ForEachStep with item key, values and Func as the next step | Range Step A RangeStep is one that executes the given step with a range of numbers. new Atom() .then(new Atom.RangeStep('n', 1, 10, new CustomStep())) .fork(); | Constructor | Description | | ----------- | ----------- | | RangeStep(String, Integer, Intger, Atom.Step) | Create a RangeStep with the item key, min value, max value and the next step | | RangeStep(String, Integer, Intger, Atom.Compute) | Create a RangeStep with the item key, min value, max value and compute as the next step | | RangeStep(String, Integer, Intger, Func) | Create a RangeStep with the item key, min value, max value and Func as the next step | Repeat Step A RepeatStep is one that repeats the given step until N times. new Atom() .then(new Atom.RepeatStep(10, new CustomStep())) .fork(); | Constructor | Description | | ----------- | ----------- | | RepeatStep(String, Integer, Atom.Step) | Create a RepeatStep with the item key, count and the next step | | RepeatStep(String, Integer, Atom.Compute) | Create a RepeatStep with the item key, count and compute as the next step | | RepeatStep(String, Integer, Func) | Create a RepeatStep with the item key, count and Func as the next step | | RepeatStep(Integer, Atom.Step) | Create a RepeatStep with count and the next step | | RepeatStep(Integer, Atom.Compute) | Create a RepeatStep with count and compute as the next step | | RepeatStep(Integer, Func) | Create a RepeatStep with count and Func as the next step | ","srcFilePath":"src/pages/docs/Step/index.md","id":"Step","location":"/docs/Step/","url":"/atom-apex/docs/Step/","childIds":["state","function"]}},"childIds":["Atom","Step","Monitor","search"]},"tutorials":{"title":"Tutorials","description":"The tutorials","url":"/atom-apex/tutorials/getting_started/step_1.html","layout":false,"content":" ","srcFilePath":"src/pages/tutorials/index.soy","id":"tutorials","location":"/tutorials/","customURL":true,"children":{"getting_started":{"title":"Getting Started","description":"The Getting Started Tutorial","tutorialTitle":"Getting started with Atom.apex","url":"/atom-apex/tutorials/getting_started/step_1.html","layout":false,"content":" ","srcFilePath":"src/pages/tutorials/getting_started/index.soy","id":"getting_started","location":"/tutorials/getting_started/","customURL":true,"children":{"step_1":{"title":"Installation","description":"Include Apex files","buttonTitle":"Done","parentId":"getting_started","layout":"tutorial","time":90,"weight":1,"content":" {$page.title} Flow.apex has a dependency on R.apex. Please install it first. Include Atom.cls, and AtomTest.cls(optional) into your Org, and you are ready to go. ","srcFilePath":"src/pages/tutorials/getting_started/step_1.md","id":"step_1","location":"/tutorials/getting_started/step_1.html","url":"/atom-apex/tutorials/getting_started/step_1.html"},"step_2":{"title":"Preliminary Knowledge","description":"Preliminary Knowledge","buttonTitle":"Done","parentId":"getting_started","layout":"tutorial","time":90,"weight":2,"content":" {$page.title} It's recommended that you have a fair amount of knowledge on R.apex, but it's not required. Atom.apex uses Func objects from R.apex, and a Func is actually a custom Apex object that mimics the behavior of a function. Here is how your implement a custom Func. public class HelloWorldFunc extends Func { public HelloWorldFunc() { super(0); // specify the number of arguments the Func takes } // Provide custom implementation for a Func that takes 0 arguments. public override Object exec() { return 'Hello World'; } } And then you instantiate, and invoke it. Func helloworld = new HelloWorldFunc(); String msg = (String)helloworld.run(); To get deeper with Func objects, please check R.apex. ","srcFilePath":"src/pages/tutorials/getting_started/step_2.md","id":"step_2","location":"/tutorials/getting_started/step_2.html","url":"/atom-apex/tutorials/getting_started/step_2.html"},"step_3":{"title":"Big Picture","description":"Big Picture","buttonTitle":"Done","parentId":"getting_started","layout":"tutorial","time":90,"weight":3,"content":" {$page.title} We will have an overview of what Atom.apex looks like in action. Map initialData = new Map{ ... }; new Atom(initialData) .then(new CustomBeforeCompute()) .then(new Atom.ForEachStep('item', 'items', new CustomCompute())) .then(new CustomAfterCompute()) .fork(); Here we split our business logic into CustomBeforeCompute, CustomCompute and CustomAfterCompute. The Atom instance is created with the initial data, then it will execute the CustomBeforeCompute. After that, it will do a 'for-each' loop of CustomCompute over the data indexed by 'items', and the looping item will be saved under the key of 'item'. Finally it will do the CustomAfterCompute. All is not set off until the fork is invoked. During any step, if governor limits are reached, a new queueable job will be created to continue to run the remaining logic. ","srcFilePath":"src/pages/tutorials/getting_started/step_3.md","id":"step_3","location":"/tutorials/getting_started/step_3.html","url":"/atom-apex/tutorials/getting_started/step_3.html"},"step_4":{"title":"Compute","description":"Compute","buttonTitle":"Done","parentId":"getting_started","layout":"tutorial","time":90,"weight":4,"content":" {$page.title} A compute is a single unsplittable unit of business logic that can be executed within a transaction. In Atom.apex, all steps are running in the queueable job context, so the Async Apex Limits apply here. Here is how we create a CustomCompute. public class CustomCompute extends Atom.Compute { public override void execute(Atom.State s) { Integer count = (Integer)s.getData('count'); count += 1; s.setData('count', count); } } ","srcFilePath":"src/pages/tutorials/getting_started/step_4.md","id":"step_4","location":"/tutorials/getting_started/step_4.html","url":"/atom-apex/tutorials/getting_started/step_4.html"},"step_5":{"title":"Step","description":"Step","buttonTitle":"Done","parentId":"getting_started","layout":"tutorial","time":90,"weight":5,"content":" {$page.title} A step is the basic component of an Atom. Here is how we chain the steps into an Atom. new Atom() .then(step1) .then(step2) .fork(); We have different kinds of steps in Atom.apex. Simple Step A simple step is one that contains a compute. The only job for a simple step is to execute that compute. new Atom() .then(new Atom.SimpleStep(new CustomCompute())) .then(new CustomCompute()) // equivalent to above .fork(); Composite Step A composite step is one that can contain multiple steps. It executes the children steps one by one. new Atom() .then( new Atom.CompositeStep() .then(...) ) .fork(); ForEach Step A ForEachStep is one that executes the 'for-each' loop with the given step. It looks up for the collection data with the given name from the Atom state, and creates a new looping item before invoking the looping step. Map initialData = new Map{ 'items' = new List{ ... } }; new Atom(initialData) .then(new Atom.ForEachStep('item', 'items', new CustomStep())) .fork(); Range Step A RangeStep is one that executes the given step with a range of numbers. new Atom() .then(new Atom.RangeStep('n', 1, 10, new CustomStep())) .fork(); Repeat Step A RepeatStep is one that repeats the given step until N times. new Atom() .then(new Atom.RepeatStep(10, new CustomStep())) .fork(); ","srcFilePath":"src/pages/tutorials/getting_started/step_5.md","id":"step_5","location":"/tutorials/getting_started/step_5.html","url":"/atom-apex/tutorials/getting_started/step_5.html"},"step_6":{"title":"State","description":"State","buttonTitle":"Done","parentId":"getting_started","layout":"tutorial","time":90,"weight":6,"content":" {$page.title} An Atom holds a state, which is then shared by all of its steps. You can access data with the state. public class CustomCompute extends Atom.Compute { public override void execute(Atom.State s) { Integer count = (Integer)s.getData('count'); count += 1; s.setData('count', count); } } And this is the only way to communicate with other steps in Atom.apex ","srcFilePath":"src/pages/tutorials/getting_started/step_6.md","id":"step_6","location":"/tutorials/getting_started/step_6.html","url":"/atom-apex/tutorials/getting_started/step_6.html"},"step_7":{"title":"Interruptions","description":"Interruptions","buttonTitle":"Done","parentId":"getting_started","layout":"tutorial","time":90,"weight":7,"content":" {$page.title} In Atom.apex, when the governor limits are reached, we try to create a new queueable job to execute the remaining logic. The attempt to switch to a new queueable job is called an interruption. Most of the time, we do not need to request for interruptions. But if we do, we can use this. public class CustomCompute extends Atom.Compute { public override void execute(Atom.State s) { s.setInterrupted(true); } } Also notice that new queueable jobs will not fix all the governor limit issues. So we have a limit for the interruptions. When an Atom tries the limit number of interruptions and still fails to resolve the resource issue, an exception will be thrown to indicate that. You can set the interruption limit. new Atom() .setMaxInterruptions(20) .then(...) .fork(); ","srcFilePath":"src/pages/tutorials/getting_started/step_7.md","id":"step_7","location":"/tutorials/getting_started/step_7.html","url":"/atom-apex/tutorials/getting_started/step_7.html"},"step_8":{"title":"Functional Support","description":"Functional Support","buttonTitle":"Done","parentId":"getting_started","layout":"tutorial","time":90,"weight":8,"content":" {$page.title} Atom.apex embraces functional style programming. You can pass in a Func to be used as a Compute or a Step. new Atom() .then(R.debug.apply('Running')) .fork(); Atom.apex has a helper Func to get values from the state, provide them to the given Func and apply the computed value back to the state. new Atom() .then(Atom.F.compute.apply('count', R.inc, 'count')) .fork(); The code above will get the number indexed by count, increment it and set it back to count. If a Func returns true without being set to the state, the interrupted flag will be marked. new Atom() .then(Atom.F.compute.apply(R.equals.apply(1), 'count')) .fork(); This code will request an interruption if the count equals with 1. ","srcFilePath":"src/pages/tutorials/getting_started/step_8.md","id":"step_8","location":"/tutorials/getting_started/step_8.html","url":"/atom-apex/tutorials/getting_started/step_8.html"},"step_9":{"title":"Monitor","description":"Monitor","buttonTitle":"Done","parentId":"getting_started","layout":"tutorial","time":90,"weight":9,"content":" {$page.title} When the governor limits are reached is determined by monitors. Atom.apex has the following built-in monitors. | Name | Description | | ---- | ----------- | | AggregateQueriesMonitor | Aggregate query limit | | CalloutsMonitor | Call out limit | | CpuTimeMonitor | Cpu time limit | | DMLRowsMonitor | DML rows limit | | DMLStatementsMonitor | DML statement limit | | EmailInvocationsMonitor | Email invocation limit | | FutureCallsMonitor | Future call limit | | HeapSizeMonitor | Heap size limit | | MobilePushApexCallsMonitor | Mobile push apex call limit | | QueriesMonitor | Query limit | | QueryLocatorRowsMonitor | Query locator row limit | | QueryRowsMonitor | Query row limit | | QueueableJobsMonitor | Queueable job limit | | SoslQueriesMonitor | SOSL query limit | Most of the time, you don't need to care about monitors. However, you could still provide your own monitor. public class CustomMonitor extends Atom.DefaultMonitor { public CustomMonitor() { super('Custom limit is reached'); } public override Integer getCurrentValue(Atom.State s) { return (Integer)s.getData('count'); } public override Integer getMaxValue(Atom.State s) { return 1000; } } Atom.registerMonitor(new CustomMonitor()); ","srcFilePath":"src/pages/tutorials/getting_started/step_9.md","id":"step_9","location":"/tutorials/getting_started/step_9.html","url":"/atom-apex/tutorials/getting_started/step_9.html"}},"childIds":["step_1","step_2","step_3","step_4","step_5","step_6","step_7","step_8","step_9"]}},"childIds":["getting_started"]}},"childIds":["docs","tutorials"]},"basePath":"/atom-apex"}