1571283300
JavaScript is the official language of all modern web browsers. As such, JavaScript questions come up in all sorts of developer interviews.
Of course these aren’t the only 3 things you should study before a JavaScript interview — there are a multitude of ways you can better prepare for an upcoming interview — but below are 3 questions an interviewer may ask to judge how well you know and understand the JavaScript language and the DOM.
So let’s get started! Note that we’re going to use vanilla JavaScript in the examples below, since your interviewer will usually want to see how well you understand JavaScript and the DOM without the help of libraries like jQuery.
When building an application, sometimes you’ll need to attach event listeners to buttons, text, or images on the page in order to perform some action when the user interacts with the element.
If we take a simple todo list as an example, the interviewer may tell you that they want an action to occur when a user clicks one of the list items. And they want you to implement this functionality in JavaScript assuming the following HTML code:
You may want to do something like the following to attach event listeners to the elements:
While this does technically work, the problem is that you’re attaching an event listener to every single item individually. This is fine for 4 elements, but what if someone adds 10,000 items (they may have a lot of things to do) to their todo list? Then your function will create 10,000 separate event listeners and attach each of them to the DOM. This isn’t very efficient.
In an interview it would be best to first ask the interviewer what the maximum number of elements the user can enter is. If it can never be more than 10, for example, then the above code would work fine. But if there’s no limit to the number of items the user can enter, then you’d want to use a more efficient solution.
If your application could end up with hundreds of event listeners, the more efficient solution would be to actually attach one event listener to the whole container, and then be able to access each item when it’s actually clicked. This is called event delegation, and it’s much more efficient than attaching separate event handlers.
Here’s the code for event delegation:
Closures are sometimes brought up in an interview so that the interviewer can gauge how familiar you are with the language, and whether you know when to implement a closure.
A closure is basically when an inner function has access to variables outside of its scope. Closures can be used for things like implementing privacy and creating function factories. A common interview question regarding the use of closures is something like this:
Write a function that will loop through a list of integers and print the index of each element after a 3 second delay.
A common (incorrect) implementation I’ve seen for this problem looks something like this:
If you run this you’ll see that you actually get the 4 printed out every time instead of the expected 0, 1, 2, 3 after a 3 second delay.
To correctly identify why this is happening it would be useful to have an understanding of why this happens in JavaScript, which is exactly what the interviewer is trying to test.
The reason for this is because the setTimeout
function creates a function (the closure) that has access to its outer scope, which is the loop that contains the index i
. After 3 seconds go by, the function is executed and it prints out the value of i
, which at the end of the loop is at 4 because it cycles through 0, 1, 2, 3, 4 and the loop finally stops at 4.
There are actually a few ways of correctly writing the function for this problem. Here are two of them:
There are some browser events that can fire many times within a short timespan very quickly, such as resizing a window or scrolling down a page. If you attach an event listener to the window scroll event for example, and the user continuously scrolls down the page very quickly, your event may fire thousands of times within the span of 3 seconds. This can cause some serious performance issues.
If you’re discussing building an application in an interview, and events like scrolling, window resizing, or key pressing come up, make sure to mention debouncing and/or throttling as a way to improve page speed and performance.
Debouncing is one way to solve this issue by limiting the time that needs to pass by until a function is called again. A correct implementation of debouncing would therefore group several function calls into one and execute it only once after some time has elapsed. Here’s an implementation in plain JavaScript that makes use of topics such as scope, closures, this, and timing events:
This function — when wrapped around an event — will execute only after a certain amount of time has elapsed.
You would use this function like so:
Throttling is another technique that’s is similar to debouncing, except that instead of waiting for some time to pass by before calling a function, throttling just spreads the function calls across a longer time interval. So if an event occurs 10 times within 100 milliseconds, throttling could spread out each of the function calls to be executed once every 2 seconds instead of all firing within 100 milliseconds.
#javascript #interview-questions
1653475560
msgpack.php
A pure PHP implementation of the MessagePack serialization format.
The recommended way to install the library is through Composer:
composer require rybakit/msgpack
To pack values you can either use an instance of a Packer
:
$packer = new Packer();
$packed = $packer->pack($value);
or call a static method on the MessagePack
class:
$packed = MessagePack::pack($value);
In the examples above, the method pack
automatically packs a value depending on its type. However, not all PHP types can be uniquely translated to MessagePack types. For example, the MessagePack format defines map
and array
types, which are represented by a single array
type in PHP. By default, the packer will pack a PHP array as a MessagePack array if it has sequential numeric keys, starting from 0
and as a MessagePack map otherwise:
$mpArr1 = $packer->pack([1, 2]); // MP array [1, 2]
$mpArr2 = $packer->pack([0 => 1, 1 => 2]); // MP array [1, 2]
$mpMap1 = $packer->pack([0 => 1, 2 => 3]); // MP map {0: 1, 2: 3}
$mpMap2 = $packer->pack([1 => 2, 2 => 3]); // MP map {1: 2, 2: 3}
$mpMap3 = $packer->pack(['a' => 1, 'b' => 2]); // MP map {a: 1, b: 2}
However, sometimes you need to pack a sequential array as a MessagePack map. To do this, use the packMap
method:
$mpMap = $packer->packMap([1, 2]); // {0: 1, 1: 2}
Here is a list of type-specific packing methods:
$packer->packNil(); // MP nil
$packer->packBool(true); // MP bool
$packer->packInt(42); // MP int
$packer->packFloat(M_PI); // MP float (32 or 64)
$packer->packFloat32(M_PI); // MP float 32
$packer->packFloat64(M_PI); // MP float 64
$packer->packStr('foo'); // MP str
$packer->packBin("\x80"); // MP bin
$packer->packArray([1, 2]); // MP array
$packer->packMap(['a' => 1]); // MP map
$packer->packExt(1, "\xaa"); // MP ext
Check the "Custom types" section below on how to pack custom types.
The Packer
object supports a number of bitmask-based options for fine-tuning the packing process (defaults are in bold):
Name | Description |
---|---|
FORCE_STR | Forces PHP strings to be packed as MessagePack UTF-8 strings |
FORCE_BIN | Forces PHP strings to be packed as MessagePack binary data |
DETECT_STR_BIN | Detects MessagePack str/bin type automatically |
FORCE_ARR | Forces PHP arrays to be packed as MessagePack arrays |
FORCE_MAP | Forces PHP arrays to be packed as MessagePack maps |
DETECT_ARR_MAP | Detects MessagePack array/map type automatically |
FORCE_FLOAT32 | Forces PHP floats to be packed as 32-bits MessagePack floats |
FORCE_FLOAT64 | Forces PHP floats to be packed as 64-bits MessagePack floats |
The type detection mode (
DETECT_STR_BIN
/DETECT_ARR_MAP
) adds some overhead which can be noticed when you pack large (16- and 32-bit) arrays or strings. However, if you know the value type in advance (for example, you only work with UTF-8 strings or/and associative arrays), you can eliminate this overhead by forcing the packer to use the appropriate type, which will save it from running the auto-detection routine. Another option is to explicitly specify the value type. The library provides 2 auxiliary classes for this,Map
andBin
. Check the "Custom types" section below for details.
Examples:
// detect str/bin type and pack PHP 64-bit floats (doubles) to MP 32-bit floats
$packer = new Packer(PackOptions::DETECT_STR_BIN | PackOptions::FORCE_FLOAT32);
// these will throw MessagePack\Exception\InvalidOptionException
$packer = new Packer(PackOptions::FORCE_STR | PackOptions::FORCE_BIN);
$packer = new Packer(PackOptions::FORCE_FLOAT32 | PackOptions::FORCE_FLOAT64);
To unpack data you can either use an instance of a BufferUnpacker
:
$unpacker = new BufferUnpacker();
$unpacker->reset($packed);
$value = $unpacker->unpack();
or call a static method on the MessagePack
class:
$value = MessagePack::unpack($packed);
If the packed data is received in chunks (e.g. when reading from a stream), use the tryUnpack
method, which attempts to unpack data and returns an array of unpacked messages (if any) instead of throwing an InsufficientDataException
:
while ($chunk = ...) {
$unpacker->append($chunk);
if ($messages = $unpacker->tryUnpack()) {
return $messages;
}
}
If you want to unpack from a specific position in a buffer, use seek
:
$unpacker->seek(42); // set position equal to 42 bytes
$unpacker->seek(-8); // set position to 8 bytes before the end of the buffer
To skip bytes from the current position, use skip
:
$unpacker->skip(10); // set position to 10 bytes ahead of the current position
To get the number of remaining (unread) bytes in the buffer:
$unreadBytesCount = $unpacker->getRemainingCount();
To check whether the buffer has unread data:
$hasUnreadBytes = $unpacker->hasRemaining();
If needed, you can remove already read data from the buffer by calling:
$releasedBytesCount = $unpacker->release();
With the read
method you can read raw (packed) data:
$packedData = $unpacker->read(2); // read 2 bytes
Besides the above methods BufferUnpacker
provides type-specific unpacking methods, namely:
$unpacker->unpackNil(); // PHP null
$unpacker->unpackBool(); // PHP bool
$unpacker->unpackInt(); // PHP int
$unpacker->unpackFloat(); // PHP float
$unpacker->unpackStr(); // PHP UTF-8 string
$unpacker->unpackBin(); // PHP binary string
$unpacker->unpackArray(); // PHP sequential array
$unpacker->unpackMap(); // PHP associative array
$unpacker->unpackExt(); // PHP MessagePack\Type\Ext object
The BufferUnpacker
object supports a number of bitmask-based options for fine-tuning the unpacking process (defaults are in bold):
Name | Description |
---|---|
BIGINT_AS_STR | Converts overflowed integers to strings [1] |
BIGINT_AS_GMP | Converts overflowed integers to GMP objects [2] |
BIGINT_AS_DEC | Converts overflowed integers to Decimal\Decimal objects [3] |
1. The binary MessagePack format has unsigned 64-bit as its largest integer data type, but PHP does not support such integers, which means that an overflow can occur during unpacking.
2. Make sure the GMP extension is enabled.
3. Make sure the Decimal extension is enabled.
Examples:
$packedUint64 = "\xcf"."\xff\xff\xff\xff"."\xff\xff\xff\xff";
$unpacker = new BufferUnpacker($packedUint64);
var_dump($unpacker->unpack()); // string(20) "18446744073709551615"
$unpacker = new BufferUnpacker($packedUint64, UnpackOptions::BIGINT_AS_GMP);
var_dump($unpacker->unpack()); // object(GMP) {...}
$unpacker = new BufferUnpacker($packedUint64, UnpackOptions::BIGINT_AS_DEC);
var_dump($unpacker->unpack()); // object(Decimal\Decimal) {...}
In addition to the basic types, the library provides functionality to serialize and deserialize arbitrary types. This can be done in several ways, depending on your use case. Let's take a look at them.
If you need to serialize an instance of one of your classes into one of the basic MessagePack types, the best way to do this is to implement the CanBePacked interface in the class. A good example of such a class is the Map
type class that comes with the library. This type is useful when you want to explicitly specify that a given PHP array should be packed as a MessagePack map without triggering an automatic type detection routine:
$packer = new Packer();
$packedMap = $packer->pack(new Map([1, 2, 3]));
$packedArray = $packer->pack([1, 2, 3]);
More type examples can be found in the src/Type directory.
As with type objects, type transformers are only responsible for serializing values. They should be used when you need to serialize a value that does not implement the CanBePacked interface. Examples of such values could be instances of built-in or third-party classes that you don't own, or non-objects such as resources.
A transformer class must implement the CanPack interface. To use a transformer, it must first be registered in the packer. Here is an example of how to serialize PHP streams into the MessagePack bin
format type using one of the supplied transformers, StreamTransformer
:
$packer = new Packer(null, [new StreamTransformer()]);
$packedBin = $packer->pack(fopen('/path/to/file', 'r+'));
More type transformer examples can be found in the src/TypeTransformer directory.
In contrast to the cases described above, extensions are intended to handle extension types and are responsible for both serialization and deserialization of values (types).
An extension class must implement the Extension interface. To use an extension, it must first be registered in the packer and the unpacker.
The MessagePack specification divides extension types into two groups: predefined and application-specific. Currently, there is only one predefined type in the specification, Timestamp.
Timestamp
The Timestamp extension type is a predefined type. Support for this type in the library is done through the TimestampExtension
class. This class is responsible for handling Timestamp
objects, which represent the number of seconds and optional adjustment in nanoseconds:
$timestampExtension = new TimestampExtension();
$packer = new Packer();
$packer = $packer->extendWith($timestampExtension);
$unpacker = new BufferUnpacker();
$unpacker = $unpacker->extendWith($timestampExtension);
$packedTimestamp = $packer->pack(Timestamp::now());
$timestamp = $unpacker->reset($packedTimestamp)->unpack();
$seconds = $timestamp->getSeconds();
$nanoseconds = $timestamp->getNanoseconds();
When using the MessagePack
class, the Timestamp extension is already registered:
$packedTimestamp = MessagePack::pack(Timestamp::now());
$timestamp = MessagePack::unpack($packedTimestamp);
Application-specific extensions
In addition, the format can be extended with your own types. For example, to make the built-in PHP DateTime
objects first-class citizens in your code, you can create a corresponding extension, as shown in the example. Please note, that custom extensions have to be registered with a unique extension ID (an integer from 0
to 127
).
More extension examples can be found in the examples/MessagePack directory.
To learn more about how extension types can be useful, check out this article.
If an error occurs during packing/unpacking, a PackingFailedException
or an UnpackingFailedException
will be thrown, respectively. In addition, an InsufficientDataException
can be thrown during unpacking.
An InvalidOptionException
will be thrown in case an invalid option (or a combination of mutually exclusive options) is used.
Run tests as follows:
vendor/bin/phpunit
Also, if you already have Docker installed, you can run the tests in a docker container. First, create a container:
./dockerfile.sh | docker build -t msgpack -
The command above will create a container named msgpack
with PHP 8.1 runtime. You may change the default runtime by defining the PHP_IMAGE
environment variable:
PHP_IMAGE='php:8.0-cli' ./dockerfile.sh | docker build -t msgpack -
See a list of various images here.
Then run the unit tests:
docker run --rm -v $PWD:/msgpack -w /msgpack msgpack
To ensure that the unpacking works correctly with malformed/semi-malformed data, you can use a testing technique called Fuzzing. The library ships with a help file (target) for PHP-Fuzzer and can be used as follows:
php-fuzzer fuzz tests/fuzz_buffer_unpacker.php
To check performance, run:
php -n -dzend_extension=opcache.so \
-dpcre.jit=1 -dopcache.enable=1 -dopcache.enable_cli=1 \
tests/bench.php
Example output
Filter: MessagePack\Tests\Perf\Filter\ListFilter
Rounds: 3
Iterations: 100000
=============================================
Test/Target Packer BufferUnpacker
---------------------------------------------
nil .................. 0.0030 ........ 0.0139
false ................ 0.0037 ........ 0.0144
true ................. 0.0040 ........ 0.0137
7-bit uint #1 ........ 0.0052 ........ 0.0120
7-bit uint #2 ........ 0.0059 ........ 0.0114
7-bit uint #3 ........ 0.0061 ........ 0.0119
5-bit sint #1 ........ 0.0067 ........ 0.0126
5-bit sint #2 ........ 0.0064 ........ 0.0132
5-bit sint #3 ........ 0.0066 ........ 0.0135
8-bit uint #1 ........ 0.0078 ........ 0.0200
8-bit uint #2 ........ 0.0077 ........ 0.0212
8-bit uint #3 ........ 0.0086 ........ 0.0203
16-bit uint #1 ....... 0.0111 ........ 0.0271
16-bit uint #2 ....... 0.0115 ........ 0.0260
16-bit uint #3 ....... 0.0103 ........ 0.0273
32-bit uint #1 ....... 0.0116 ........ 0.0326
32-bit uint #2 ....... 0.0118 ........ 0.0332
32-bit uint #3 ....... 0.0127 ........ 0.0325
64-bit uint #1 ....... 0.0140 ........ 0.0277
64-bit uint #2 ....... 0.0134 ........ 0.0294
64-bit uint #3 ....... 0.0134 ........ 0.0281
8-bit int #1 ......... 0.0086 ........ 0.0241
8-bit int #2 ......... 0.0089 ........ 0.0225
8-bit int #3 ......... 0.0085 ........ 0.0229
16-bit int #1 ........ 0.0118 ........ 0.0280
16-bit int #2 ........ 0.0121 ........ 0.0270
16-bit int #3 ........ 0.0109 ........ 0.0274
32-bit int #1 ........ 0.0128 ........ 0.0346
32-bit int #2 ........ 0.0118 ........ 0.0339
32-bit int #3 ........ 0.0135 ........ 0.0368
64-bit int #1 ........ 0.0138 ........ 0.0276
64-bit int #2 ........ 0.0132 ........ 0.0286
64-bit int #3 ........ 0.0137 ........ 0.0274
64-bit int #4 ........ 0.0180 ........ 0.0285
64-bit float #1 ...... 0.0134 ........ 0.0284
64-bit float #2 ...... 0.0125 ........ 0.0275
64-bit float #3 ...... 0.0126 ........ 0.0283
fix string #1 ........ 0.0035 ........ 0.0133
fix string #2 ........ 0.0094 ........ 0.0216
fix string #3 ........ 0.0094 ........ 0.0222
fix string #4 ........ 0.0091 ........ 0.0241
8-bit string #1 ...... 0.0122 ........ 0.0301
8-bit string #2 ...... 0.0118 ........ 0.0304
8-bit string #3 ...... 0.0119 ........ 0.0315
16-bit string #1 ..... 0.0150 ........ 0.0388
16-bit string #2 ..... 0.1545 ........ 0.1665
32-bit string ........ 0.1570 ........ 0.1756
wide char string #1 .. 0.0091 ........ 0.0236
wide char string #2 .. 0.0122 ........ 0.0313
8-bit binary #1 ...... 0.0100 ........ 0.0302
8-bit binary #2 ...... 0.0123 ........ 0.0324
8-bit binary #3 ...... 0.0126 ........ 0.0327
16-bit binary ........ 0.0168 ........ 0.0372
32-bit binary ........ 0.1588 ........ 0.1754
fix array #1 ......... 0.0042 ........ 0.0131
fix array #2 ......... 0.0294 ........ 0.0367
fix array #3 ......... 0.0412 ........ 0.0472
16-bit array #1 ...... 0.1378 ........ 0.1596
16-bit array #2 ........... S ............. S
32-bit array .............. S ............. S
complex array ........ 0.1865 ........ 0.2283
fix map #1 ........... 0.0725 ........ 0.1048
fix map #2 ........... 0.0319 ........ 0.0405
fix map #3 ........... 0.0356 ........ 0.0665
fix map #4 ........... 0.0465 ........ 0.0497
16-bit map #1 ........ 0.2540 ........ 0.3028
16-bit map #2 ............. S ............. S
32-bit map ................ S ............. S
complex map .......... 0.2372 ........ 0.2710
fixext 1 ............. 0.0283 ........ 0.0358
fixext 2 ............. 0.0291 ........ 0.0371
fixext 4 ............. 0.0302 ........ 0.0355
fixext 8 ............. 0.0288 ........ 0.0384
fixext 16 ............ 0.0293 ........ 0.0359
8-bit ext ............ 0.0302 ........ 0.0439
16-bit ext ........... 0.0334 ........ 0.0499
32-bit ext ........... 0.1845 ........ 0.1888
32-bit timestamp #1 .. 0.0337 ........ 0.0547
32-bit timestamp #2 .. 0.0335 ........ 0.0560
64-bit timestamp #1 .. 0.0371 ........ 0.0575
64-bit timestamp #2 .. 0.0374 ........ 0.0542
64-bit timestamp #3 .. 0.0356 ........ 0.0533
96-bit timestamp #1 .. 0.0362 ........ 0.0699
96-bit timestamp #2 .. 0.0381 ........ 0.0701
96-bit timestamp #3 .. 0.0367 ........ 0.0687
=============================================
Total 2.7618 4.0820
Skipped 4 4
Failed 0 0
Ignored 0 0
With JIT:
php -n -dzend_extension=opcache.so \
-dpcre.jit=1 -dopcache.jit_buffer_size=64M -dopcache.jit=tracing -dopcache.enable=1 -dopcache.enable_cli=1 \
tests/bench.php
Example output
Filter: MessagePack\Tests\Perf\Filter\ListFilter
Rounds: 3
Iterations: 100000
=============================================
Test/Target Packer BufferUnpacker
---------------------------------------------
nil .................. 0.0005 ........ 0.0054
false ................ 0.0004 ........ 0.0059
true ................. 0.0004 ........ 0.0059
7-bit uint #1 ........ 0.0010 ........ 0.0047
7-bit uint #2 ........ 0.0010 ........ 0.0046
7-bit uint #3 ........ 0.0010 ........ 0.0046
5-bit sint #1 ........ 0.0025 ........ 0.0046
5-bit sint #2 ........ 0.0023 ........ 0.0046
5-bit sint #3 ........ 0.0024 ........ 0.0045
8-bit uint #1 ........ 0.0043 ........ 0.0081
8-bit uint #2 ........ 0.0043 ........ 0.0079
8-bit uint #3 ........ 0.0041 ........ 0.0080
16-bit uint #1 ....... 0.0064 ........ 0.0095
16-bit uint #2 ....... 0.0064 ........ 0.0091
16-bit uint #3 ....... 0.0064 ........ 0.0094
32-bit uint #1 ....... 0.0085 ........ 0.0114
32-bit uint #2 ....... 0.0077 ........ 0.0122
32-bit uint #3 ....... 0.0077 ........ 0.0120
64-bit uint #1 ....... 0.0085 ........ 0.0159
64-bit uint #2 ....... 0.0086 ........ 0.0157
64-bit uint #3 ....... 0.0086 ........ 0.0158
8-bit int #1 ......... 0.0042 ........ 0.0080
8-bit int #2 ......... 0.0042 ........ 0.0080
8-bit int #3 ......... 0.0042 ........ 0.0081
16-bit int #1 ........ 0.0065 ........ 0.0095
16-bit int #2 ........ 0.0065 ........ 0.0090
16-bit int #3 ........ 0.0056 ........ 0.0085
32-bit int #1 ........ 0.0067 ........ 0.0107
32-bit int #2 ........ 0.0066 ........ 0.0106
32-bit int #3 ........ 0.0063 ........ 0.0104
64-bit int #1 ........ 0.0072 ........ 0.0162
64-bit int #2 ........ 0.0073 ........ 0.0174
64-bit int #3 ........ 0.0072 ........ 0.0164
64-bit int #4 ........ 0.0077 ........ 0.0161
64-bit float #1 ...... 0.0053 ........ 0.0135
64-bit float #2 ...... 0.0053 ........ 0.0135
64-bit float #3 ...... 0.0052 ........ 0.0135
fix string #1 ....... -0.0002 ........ 0.0044
fix string #2 ........ 0.0035 ........ 0.0067
fix string #3 ........ 0.0035 ........ 0.0077
fix string #4 ........ 0.0033 ........ 0.0078
8-bit string #1 ...... 0.0059 ........ 0.0110
8-bit string #2 ...... 0.0063 ........ 0.0121
8-bit string #3 ...... 0.0064 ........ 0.0124
16-bit string #1 ..... 0.0099 ........ 0.0146
16-bit string #2 ..... 0.1522 ........ 0.1474
32-bit string ........ 0.1511 ........ 0.1483
wide char string #1 .. 0.0039 ........ 0.0084
wide char string #2 .. 0.0073 ........ 0.0123
8-bit binary #1 ...... 0.0040 ........ 0.0112
8-bit binary #2 ...... 0.0075 ........ 0.0123
8-bit binary #3 ...... 0.0077 ........ 0.0129
16-bit binary ........ 0.0096 ........ 0.0145
32-bit binary ........ 0.1535 ........ 0.1479
fix array #1 ......... 0.0008 ........ 0.0061
fix array #2 ......... 0.0121 ........ 0.0165
fix array #3 ......... 0.0193 ........ 0.0222
16-bit array #1 ...... 0.0607 ........ 0.0479
16-bit array #2 ........... S ............. S
32-bit array .............. S ............. S
complex array ........ 0.0749 ........ 0.0824
fix map #1 ........... 0.0329 ........ 0.0431
fix map #2 ........... 0.0161 ........ 0.0189
fix map #3 ........... 0.0205 ........ 0.0262
fix map #4 ........... 0.0252 ........ 0.0205
16-bit map #1 ........ 0.1016 ........ 0.0927
16-bit map #2 ............. S ............. S
32-bit map ................ S ............. S
complex map .......... 0.1096 ........ 0.1030
fixext 1 ............. 0.0157 ........ 0.0161
fixext 2 ............. 0.0175 ........ 0.0183
fixext 4 ............. 0.0156 ........ 0.0185
fixext 8 ............. 0.0163 ........ 0.0184
fixext 16 ............ 0.0164 ........ 0.0182
8-bit ext ............ 0.0158 ........ 0.0207
16-bit ext ........... 0.0203 ........ 0.0219
32-bit ext ........... 0.1614 ........ 0.1539
32-bit timestamp #1 .. 0.0195 ........ 0.0249
32-bit timestamp #2 .. 0.0188 ........ 0.0260
64-bit timestamp #1 .. 0.0207 ........ 0.0281
64-bit timestamp #2 .. 0.0212 ........ 0.0291
64-bit timestamp #3 .. 0.0207 ........ 0.0295
96-bit timestamp #1 .. 0.0222 ........ 0.0358
96-bit timestamp #2 .. 0.0228 ........ 0.0353
96-bit timestamp #3 .. 0.0210 ........ 0.0319
=============================================
Total 1.6432 1.9674
Skipped 4 4
Failed 0 0
Ignored 0 0
You may change default benchmark settings by defining the following environment variables:
Name | Default |
---|---|
MP_BENCH_TARGETS | pure_p,pure_u , see a list of available targets |
MP_BENCH_ITERATIONS | 100_000 |
MP_BENCH_DURATION | not set |
MP_BENCH_ROUNDS | 3 |
MP_BENCH_TESTS | -@slow , see a list of available tests |
For example:
export MP_BENCH_TARGETS=pure_p
export MP_BENCH_ITERATIONS=1000000
export MP_BENCH_ROUNDS=5
# a comma separated list of test names
export MP_BENCH_TESTS='complex array, complex map'
# or a group name
# export MP_BENCH_TESTS='-@slow' // @pecl_comp
# or a regexp
# export MP_BENCH_TESTS='/complex (array|map)/'
Another example, benchmarking both the library and the PECL extension:
MP_BENCH_TARGETS=pure_p,pure_u,pecl_p,pecl_u \
php -n -dextension=msgpack.so -dzend_extension=opcache.so \
-dpcre.jit=1 -dopcache.enable=1 -dopcache.enable_cli=1 \
tests/bench.php
Example output
Filter: MessagePack\Tests\Perf\Filter\ListFilter
Rounds: 3
Iterations: 100000
===========================================================================
Test/Target Packer BufferUnpacker msgpack_pack msgpack_unpack
---------------------------------------------------------------------------
nil .................. 0.0031 ........ 0.0141 ...... 0.0055 ........ 0.0064
false ................ 0.0039 ........ 0.0154 ...... 0.0056 ........ 0.0053
true ................. 0.0038 ........ 0.0139 ...... 0.0056 ........ 0.0044
7-bit uint #1 ........ 0.0061 ........ 0.0110 ...... 0.0059 ........ 0.0046
7-bit uint #2 ........ 0.0065 ........ 0.0119 ...... 0.0042 ........ 0.0029
7-bit uint #3 ........ 0.0054 ........ 0.0117 ...... 0.0045 ........ 0.0025
5-bit sint #1 ........ 0.0047 ........ 0.0103 ...... 0.0038 ........ 0.0022
5-bit sint #2 ........ 0.0048 ........ 0.0117 ...... 0.0038 ........ 0.0022
5-bit sint #3 ........ 0.0046 ........ 0.0102 ...... 0.0038 ........ 0.0023
8-bit uint #1 ........ 0.0063 ........ 0.0174 ...... 0.0039 ........ 0.0031
8-bit uint #2 ........ 0.0063 ........ 0.0167 ...... 0.0040 ........ 0.0029
8-bit uint #3 ........ 0.0063 ........ 0.0168 ...... 0.0039 ........ 0.0030
16-bit uint #1 ....... 0.0092 ........ 0.0222 ...... 0.0049 ........ 0.0030
16-bit uint #2 ....... 0.0096 ........ 0.0227 ...... 0.0042 ........ 0.0046
16-bit uint #3 ....... 0.0123 ........ 0.0274 ...... 0.0059 ........ 0.0051
32-bit uint #1 ....... 0.0136 ........ 0.0331 ...... 0.0060 ........ 0.0048
32-bit uint #2 ....... 0.0130 ........ 0.0336 ...... 0.0070 ........ 0.0048
32-bit uint #3 ....... 0.0127 ........ 0.0329 ...... 0.0051 ........ 0.0048
64-bit uint #1 ....... 0.0126 ........ 0.0268 ...... 0.0055 ........ 0.0049
64-bit uint #2 ....... 0.0135 ........ 0.0281 ...... 0.0052 ........ 0.0046
64-bit uint #3 ....... 0.0131 ........ 0.0274 ...... 0.0069 ........ 0.0044
8-bit int #1 ......... 0.0077 ........ 0.0236 ...... 0.0058 ........ 0.0044
8-bit int #2 ......... 0.0087 ........ 0.0244 ...... 0.0058 ........ 0.0048
8-bit int #3 ......... 0.0084 ........ 0.0241 ...... 0.0055 ........ 0.0049
16-bit int #1 ........ 0.0112 ........ 0.0271 ...... 0.0048 ........ 0.0045
16-bit int #2 ........ 0.0124 ........ 0.0292 ...... 0.0057 ........ 0.0049
16-bit int #3 ........ 0.0118 ........ 0.0270 ...... 0.0058 ........ 0.0050
32-bit int #1 ........ 0.0137 ........ 0.0366 ...... 0.0058 ........ 0.0051
32-bit int #2 ........ 0.0133 ........ 0.0366 ...... 0.0056 ........ 0.0049
32-bit int #3 ........ 0.0129 ........ 0.0350 ...... 0.0052 ........ 0.0048
64-bit int #1 ........ 0.0145 ........ 0.0254 ...... 0.0034 ........ 0.0025
64-bit int #2 ........ 0.0097 ........ 0.0214 ...... 0.0034 ........ 0.0025
64-bit int #3 ........ 0.0096 ........ 0.0287 ...... 0.0059 ........ 0.0050
64-bit int #4 ........ 0.0143 ........ 0.0277 ...... 0.0059 ........ 0.0046
64-bit float #1 ...... 0.0134 ........ 0.0281 ...... 0.0057 ........ 0.0052
64-bit float #2 ...... 0.0141 ........ 0.0281 ...... 0.0057 ........ 0.0050
64-bit float #3 ...... 0.0144 ........ 0.0282 ...... 0.0057 ........ 0.0050
fix string #1 ........ 0.0036 ........ 0.0143 ...... 0.0066 ........ 0.0053
fix string #2 ........ 0.0107 ........ 0.0222 ...... 0.0065 ........ 0.0068
fix string #3 ........ 0.0116 ........ 0.0245 ...... 0.0063 ........ 0.0069
fix string #4 ........ 0.0105 ........ 0.0253 ...... 0.0083 ........ 0.0077
8-bit string #1 ...... 0.0126 ........ 0.0318 ...... 0.0075 ........ 0.0088
8-bit string #2 ...... 0.0121 ........ 0.0295 ...... 0.0076 ........ 0.0086
8-bit string #3 ...... 0.0125 ........ 0.0293 ...... 0.0130 ........ 0.0093
16-bit string #1 ..... 0.0159 ........ 0.0368 ...... 0.0117 ........ 0.0086
16-bit string #2 ..... 0.1547 ........ 0.1686 ...... 0.1516 ........ 0.1373
32-bit string ........ 0.1558 ........ 0.1729 ...... 0.1511 ........ 0.1396
wide char string #1 .. 0.0098 ........ 0.0237 ...... 0.0066 ........ 0.0065
wide char string #2 .. 0.0128 ........ 0.0291 ...... 0.0061 ........ 0.0082
8-bit binary #1 ........... I ............. I ........... F ............. I
8-bit binary #2 ........... I ............. I ........... F ............. I
8-bit binary #3 ........... I ............. I ........... F ............. I
16-bit binary ............. I ............. I ........... F ............. I
32-bit binary ............. I ............. I ........... F ............. I
fix array #1 ......... 0.0040 ........ 0.0129 ...... 0.0120 ........ 0.0058
fix array #2 ......... 0.0279 ........ 0.0390 ...... 0.0143 ........ 0.0165
fix array #3 ......... 0.0415 ........ 0.0463 ...... 0.0162 ........ 0.0187
16-bit array #1 ...... 0.1349 ........ 0.1628 ...... 0.0334 ........ 0.0341
16-bit array #2 ........... S ............. S ........... S ............. S
32-bit array .............. S ............. S ........... S ............. S
complex array ............. I ............. I ........... F ............. F
fix map #1 ................ I ............. I ........... F ............. I
fix map #2 ........... 0.0345 ........ 0.0391 ...... 0.0143 ........ 0.0168
fix map #3 ................ I ............. I ........... F ............. I
fix map #4 ........... 0.0459 ........ 0.0473 ...... 0.0151 ........ 0.0163
16-bit map #1 ........ 0.2518 ........ 0.2962 ...... 0.0400 ........ 0.0490
16-bit map #2 ............. S ............. S ........... S ............. S
32-bit map ................ S ............. S ........... S ............. S
complex map .......... 0.2380 ........ 0.2682 ...... 0.0545 ........ 0.0579
fixext 1 .................. I ............. I ........... F ............. F
fixext 2 .................. I ............. I ........... F ............. F
fixext 4 .................. I ............. I ........... F ............. F
fixext 8 .................. I ............. I ........... F ............. F
fixext 16 ................. I ............. I ........... F ............. F
8-bit ext ................. I ............. I ........... F ............. F
16-bit ext ................ I ............. I ........... F ............. F
32-bit ext ................ I ............. I ........... F ............. F
32-bit timestamp #1 ....... I ............. I ........... F ............. F
32-bit timestamp #2 ....... I ............. I ........... F ............. F
64-bit timestamp #1 ....... I ............. I ........... F ............. F
64-bit timestamp #2 ....... I ............. I ........... F ............. F
64-bit timestamp #3 ....... I ............. I ........... F ............. F
96-bit timestamp #1 ....... I ............. I ........... F ............. F
96-bit timestamp #2 ....... I ............. I ........... F ............. F
96-bit timestamp #3 ....... I ............. I ........... F ............. F
===========================================================================
Total 1.5625 2.3866 0.7735 0.7243
Skipped 4 4 4 4
Failed 0 0 24 17
Ignored 24 24 0 7
With JIT:
MP_BENCH_TARGETS=pure_p,pure_u,pecl_p,pecl_u \
php -n -dextension=msgpack.so -dzend_extension=opcache.so \
-dpcre.jit=1 -dopcache.jit_buffer_size=64M -dopcache.jit=tracing -dopcache.enable=1 -dopcache.enable_cli=1 \
tests/bench.php
Example output
Filter: MessagePack\Tests\Perf\Filter\ListFilter
Rounds: 3
Iterations: 100000
===========================================================================
Test/Target Packer BufferUnpacker msgpack_pack msgpack_unpack
---------------------------------------------------------------------------
nil .................. 0.0001 ........ 0.0052 ...... 0.0053 ........ 0.0042
false ................ 0.0007 ........ 0.0060 ...... 0.0057 ........ 0.0043
true ................. 0.0008 ........ 0.0060 ...... 0.0056 ........ 0.0041
7-bit uint #1 ........ 0.0031 ........ 0.0046 ...... 0.0062 ........ 0.0041
7-bit uint #2 ........ 0.0021 ........ 0.0043 ...... 0.0062 ........ 0.0041
7-bit uint #3 ........ 0.0022 ........ 0.0044 ...... 0.0061 ........ 0.0040
5-bit sint #1 ........ 0.0030 ........ 0.0048 ...... 0.0062 ........ 0.0040
5-bit sint #2 ........ 0.0032 ........ 0.0046 ...... 0.0062 ........ 0.0040
5-bit sint #3 ........ 0.0031 ........ 0.0046 ...... 0.0062 ........ 0.0040
8-bit uint #1 ........ 0.0054 ........ 0.0079 ...... 0.0062 ........ 0.0050
8-bit uint #2 ........ 0.0051 ........ 0.0079 ...... 0.0064 ........ 0.0044
8-bit uint #3 ........ 0.0051 ........ 0.0082 ...... 0.0062 ........ 0.0044
16-bit uint #1 ....... 0.0077 ........ 0.0094 ...... 0.0065 ........ 0.0045
16-bit uint #2 ....... 0.0077 ........ 0.0094 ...... 0.0063 ........ 0.0045
16-bit uint #3 ....... 0.0077 ........ 0.0095 ...... 0.0064 ........ 0.0047
32-bit uint #1 ....... 0.0088 ........ 0.0119 ...... 0.0063 ........ 0.0043
32-bit uint #2 ....... 0.0089 ........ 0.0117 ...... 0.0062 ........ 0.0039
32-bit uint #3 ....... 0.0089 ........ 0.0118 ...... 0.0063 ........ 0.0044
64-bit uint #1 ....... 0.0097 ........ 0.0155 ...... 0.0063 ........ 0.0045
64-bit uint #2 ....... 0.0095 ........ 0.0153 ...... 0.0061 ........ 0.0045
64-bit uint #3 ....... 0.0096 ........ 0.0156 ...... 0.0063 ........ 0.0047
8-bit int #1 ......... 0.0053 ........ 0.0083 ...... 0.0062 ........ 0.0044
8-bit int #2 ......... 0.0052 ........ 0.0080 ...... 0.0062 ........ 0.0044
8-bit int #3 ......... 0.0052 ........ 0.0080 ...... 0.0062 ........ 0.0043
16-bit int #1 ........ 0.0089 ........ 0.0097 ...... 0.0069 ........ 0.0046
16-bit int #2 ........ 0.0075 ........ 0.0093 ...... 0.0063 ........ 0.0043
16-bit int #3 ........ 0.0075 ........ 0.0094 ...... 0.0062 ........ 0.0046
32-bit int #1 ........ 0.0086 ........ 0.0122 ...... 0.0063 ........ 0.0044
32-bit int #2 ........ 0.0087 ........ 0.0120 ...... 0.0066 ........ 0.0046
32-bit int #3 ........ 0.0086 ........ 0.0121 ...... 0.0060 ........ 0.0044
64-bit int #1 ........ 0.0096 ........ 0.0149 ...... 0.0060 ........ 0.0045
64-bit int #2 ........ 0.0096 ........ 0.0157 ...... 0.0062 ........ 0.0044
64-bit int #3 ........ 0.0096 ........ 0.0160 ...... 0.0063 ........ 0.0046
64-bit int #4 ........ 0.0097 ........ 0.0157 ...... 0.0061 ........ 0.0044
64-bit float #1 ...... 0.0079 ........ 0.0153 ...... 0.0056 ........ 0.0044
64-bit float #2 ...... 0.0079 ........ 0.0152 ...... 0.0057 ........ 0.0045
64-bit float #3 ...... 0.0079 ........ 0.0155 ...... 0.0057 ........ 0.0044
fix string #1 ........ 0.0010 ........ 0.0045 ...... 0.0071 ........ 0.0044
fix string #2 ........ 0.0048 ........ 0.0075 ...... 0.0070 ........ 0.0060
fix string #3 ........ 0.0048 ........ 0.0086 ...... 0.0068 ........ 0.0060
fix string #4 ........ 0.0050 ........ 0.0088 ...... 0.0070 ........ 0.0059
8-bit string #1 ...... 0.0081 ........ 0.0129 ...... 0.0069 ........ 0.0062
8-bit string #2 ...... 0.0086 ........ 0.0128 ...... 0.0069 ........ 0.0065
8-bit string #3 ...... 0.0086 ........ 0.0126 ...... 0.0115 ........ 0.0065
16-bit string #1 ..... 0.0105 ........ 0.0137 ...... 0.0128 ........ 0.0068
16-bit string #2 ..... 0.1510 ........ 0.1486 ...... 0.1526 ........ 0.1391
32-bit string ........ 0.1517 ........ 0.1475 ...... 0.1504 ........ 0.1370
wide char string #1 .. 0.0044 ........ 0.0085 ...... 0.0067 ........ 0.0057
wide char string #2 .. 0.0081 ........ 0.0125 ...... 0.0069 ........ 0.0063
8-bit binary #1 ........... I ............. I ........... F ............. I
8-bit binary #2 ........... I ............. I ........... F ............. I
8-bit binary #3 ........... I ............. I ........... F ............. I
16-bit binary ............. I ............. I ........... F ............. I
32-bit binary ............. I ............. I ........... F ............. I
fix array #1 ......... 0.0014 ........ 0.0059 ...... 0.0132 ........ 0.0055
fix array #2 ......... 0.0146 ........ 0.0156 ...... 0.0155 ........ 0.0148
fix array #3 ......... 0.0211 ........ 0.0229 ...... 0.0179 ........ 0.0180
16-bit array #1 ...... 0.0673 ........ 0.0498 ...... 0.0343 ........ 0.0388
16-bit array #2 ........... S ............. S ........... S ............. S
32-bit array .............. S ............. S ........... S ............. S
complex array ............. I ............. I ........... F ............. F
fix map #1 ................ I ............. I ........... F ............. I
fix map #2 ........... 0.0148 ........ 0.0180 ...... 0.0156 ........ 0.0179
fix map #3 ................ I ............. I ........... F ............. I
fix map #4 ........... 0.0252 ........ 0.0201 ...... 0.0214 ........ 0.0167
16-bit map #1 ........ 0.1027 ........ 0.0836 ...... 0.0388 ........ 0.0510
16-bit map #2 ............. S ............. S ........... S ............. S
32-bit map ................ S ............. S ........... S ............. S
complex map .......... 0.1104 ........ 0.1010 ...... 0.0556 ........ 0.0602
fixext 1 .................. I ............. I ........... F ............. F
fixext 2 .................. I ............. I ........... F ............. F
fixext 4 .................. I ............. I ........... F ............. F
fixext 8 .................. I ............. I ........... F ............. F
fixext 16 ................. I ............. I ........... F ............. F
8-bit ext ................. I ............. I ........... F ............. F
16-bit ext ................ I ............. I ........... F ............. F
32-bit ext ................ I ............. I ........... F ............. F
32-bit timestamp #1 ....... I ............. I ........... F ............. F
32-bit timestamp #2 ....... I ............. I ........... F ............. F
64-bit timestamp #1 ....... I ............. I ........... F ............. F
64-bit timestamp #2 ....... I ............. I ........... F ............. F
64-bit timestamp #3 ....... I ............. I ........... F ............. F
96-bit timestamp #1 ....... I ............. I ........... F ............. F
96-bit timestamp #2 ....... I ............. I ........... F ............. F
96-bit timestamp #3 ....... I ............. I ........... F ............. F
===========================================================================
Total 0.9642 1.0909 0.8224 0.7213
Skipped 4 4 4 4
Failed 0 0 24 17
Ignored 24 24 0 7
Note that the msgpack extension (v2.1.2) doesn't support ext, bin and UTF-8 str types.
The library is released under the MIT License. See the bundled LICENSE file for details.
Author: rybakit
Source Code: https://github.com/rybakit/msgpack.php
License: MIT License
1675304280
We are back with another exciting and much-talked-about Rails tutorial on how to use Hotwire with the Rails application. This Hotwire Rails tutorial is an alternate method for building modern web applications that consume a pinch of JavaScript.
Rails 7 Hotwire is the default front-end framework shipped with Rails 7 after it was launched. It is used to represent HTML over the wire in the Rails application. Previously, we used to add a hotwire-rails gem in our gem file and then run rails hotwire: install. However, with the introduction of Rails 7, the gem got deprecated. Now, we use turbo-rails and stimulus rails directly, which work as Hotwire’s SPA-like page accelerator and Hotwire’s modest JavaScript framework.
Hotwire is a package of different frameworks that help to build applications. It simplifies the developer’s work for writing web pages without the need to write JavaScript, and instead sending HTML code over the wire.
Introduction to The Hotwire Framework:
It uses simplified techniques to build web applications while decreasing the usage of JavaScript in the application. Turbo offers numerous handling methods for the HTML data sent over the wire and displaying the application’s data without actually loading the entire page. It helps to maintain the simplicity of web applications without destroying the single-page application experience by using the below techniques:
Turbo Frames: Turbo Frames help to load the different sections of our markup without any dependency as it divides the page into different contexts separately called frames and updates these frames individually.
Turbo Drive: Every link doesn’t have to make the entire page reload when clicked. Only the HTML contained within the tag will be displayed.
Turbo Streams: To add real-time features to the application, this technique is used. It helps to bring real-time data to the application using CRUD actions.
It represents the JavaScript framework, which is required when JS is a requirement in the application. The interaction with the HTML is possible with the help of a stimulus, as the controllers that help those interactions are written by a stimulus.
Not much information is available about Strada as it has not been officially released yet. However, it works with native applications, and by using HTML bridge attributes, interaction is made possible between web applications and native apps.
Simple diagrammatic representation of Hotwire Stack:
As we are implementing the Ruby on Rails Hotwire tutorial, make sure about the following installations before you can get started.
Looking for an enthusiastic team of ROR developers to shape the vision of your web project?
Contact Bacancy today and hire Ruby developers to start building your dream project!
Find the following commands to create a rails application.
mkdir ~/projects/railshotwire
cd ~/projects/railshotwire
echo "source 'https://rubygems.org'" > Gemfile
echo "gem 'rails', '~> 7.0.0'" >> Gemfile
bundle install
bundle exec rails new . --force -d=postgresql
Now create some files for the project, up till now no usage of Rails Hotwire can be seen.
Fire the following command in your terminal.
echo "class HomeController < ApplicationController" > app/controllers/home_controller.rb
echo "end" >> app/controllers/home_controller.rb
echo "class OtherController < ApplicationController" > app/controllers/other_controller.rb
echo "end" >> app/controllers/home_controller.rb
echo "Rails.application.routes.draw do" > config/routes.rb
echo ' get "home/index"' >> config/routes.rb
echo ' get "other/index"' >> config/routes.rb
echo ' root to: "home#index"' >> config/routes.rb
echo 'end' >> config/routes.rb
mkdir app/views/home
echo '<h1>This is Rails Hotwire homepage</h1>' > app/views/home/index.html.erb
echo '<div><%= link_to "Enter to other page", other_index_path %></div>' >> app/views/home/index.html.erb
mkdir app/views/other
echo '<h1>This is Another page</h1>' > app/views/other/index.html.erb
echo '<div><%= link_to "Enter to home page", root_path %></div>' >> app/views/other/index.html.erb
bin/rails db:create
bin/rails db:migrate
Additionally, you can clone the code and browse through the project. Here’s the source code of the repository: Rails 7 Hotwire application
Now, let’s see how Hotwire Rails can work its magic with various Turbo techniques.
Go to your localhost:3000 on your web browser and right-click on the Inspect and open a Network tab of the DevTools of the browser.
Now click on go to another page link that appears on the home page to redirect from the home page to another page. In our Network tab, we can see that this action of navigation is achieved via XHR. It appears only the part inside HTML is reloaded, here neither the CSS is reloaded nor the JS is reloaded when the navigation action is performed.
By performing this action we can see that Turbo Drive helps to represent the HTML response without loading the full page and only follows redirect and reindeer HTML responses which helps to make the application faster to access.
This technique helps to divide the current page into different sections called frames that can be updated separately independently when new data is added from the server.
Below we discuss the different use cases of Turbo frame like inline edition, sorting, searching, and filtering of data.
Let’s perform some practical actions to see the example of these use cases.
Make changes in the app/controllers/home_controller.rb file
#CODE
class HomeController < ApplicationController
def turbo_frame_form
end
def turbo_frame submit
extracted_anynumber = params[:any][:anynumber]
render :turbo_frame_form, status: :ok, locals: {anynumber: extracted_anynumber, comment: 'turbo_frame_submit ok' }
end
end
Add app/views/home/turbo_frame_form.html.erb file to the application and add this content inside the file.
#CODE
<section>
<%= turbo_frame_tag 'anyframe' do %>
<div>
<h2>Frame view</h2>
<%= form_with scope: :any, url: turbo_frame_submit_path, local: true do |form| %>
<%= form.label :anynumber, 'Type an integer (odd or even)', 'class' => 'my-0 d-inline' %>
<%= form.text_field :anynumber, type: 'number', 'required' => 'true', 'value' => "#{local_assigns[:anynumber] || 0}", 'aria-describedby' => 'anynumber' %>
<%= form.submit 'Submit this number', 'id' => 'submit-number' %>
<% end %>
</div>
<div>
<h2>Data of the view</h2>
<pre style="font-size: .7rem;"><%= JSON.pretty_generate(local_assigns) %></pre>
</div>
<% end %>
</section>
Make some adjustments in routes.rb
#CODE
Rails.application.routes.draw do
get 'home/index'
get 'other/index'
get '/home/turbo_frame_form' => 'home#turbo_frame_form', as: 'turbo_frame_form'
post '/home/turbo_frame_submit' => 'home#turbo_frame_submit', as: 'turbo_frame_submit'
root to: "home#index"
end
#CODE
<h1>This is Rails Hotwire home page</h1>
<div><%= link_to "Enter to other page", other_index_path %></div>
<%= turbo_frame_tag 'anyframe' do %>
<div>
<h2>Home view</h2>
<%= form_with scope: :any, url: turbo_frame_submit_path, local: true do |form| %>
<%= form.label :anynumber, 'Type an integer (odd or even)', 'class' => 'my-0 d-inline' %>
<%= form.text_field :anynumber, type: 'number', 'required' => 'true', 'value' => "#{local_assigns[:anynumber] || 0}", 'aria-describedby' => 'anynumber' %>
<%= form.submit 'Submit this number', 'id' => 'submit-number' %>
<% end %>
<div>
<% end %>
After making all the changes, restart the rails server and refresh the browser, the default view will appear on the browser.
Now in the field enter any digit, after entering the digit click on submit button, and as the submit button is clicked we can see the Turbo Frame in action in the below screen, we can observe that the frame part changed, the first title and first link didn’t move.
Turbo Streams deliver page updates over WebSocket, SSE or in response to form submissions by only using HTML and a series of CRUD-like operations, you are free to say that either
This transmit can be represented by a simple example.
#CODE
class OtherController < ApplicationController
def post_something
respond_to do |format|
format.turbo_stream { }
end
end
end
Add the below line in routes.rb file of the application
#CODE
post '/other/post_something' => 'other#post_something', as: 'post_something'
Superb! Rails will now attempt to locate the app/views/other/post_something.turbo_stream.erb template at any moment the ‘/other/post_something’ endpoint is reached.
For this, we need to add app/views/other/post_something.turbo_stream.erb template in the rails application.
#CODE
<turbo-stream action="append" target="messages">
<template>
<div id="message_1">This changes the existing message!</div>
</template>
</turbo-stream>
This states that the response will try to append the template of the turbo frame with ID “messages”.
Now change the index.html.erb file in app/views/other paths with the below content.
#CODE
<h1>This is Another page</h1>
<div><%= link_to "Enter to home page", root_path %></div>
<div style="margin-top: 3rem;">
<%= form_with scope: :any, url: post_something_path do |form| %>
<%= form.submit 'Post any message %>
<% end %>
<turbo-frame id="messages">
<div>An empty message</div>
</turbo-frame>
</div>
This action shows that after submitting the response, the Turbo Streams help the developer to append the message, without reloading the page.
Another use case we can test is that rather than appending the message, the developer replaces the message. For that, we need to change the content of app/views/other/post_something.turbo_stream.erb template file and change the value of the action attribute from append to replace and check the changes in the browser.
#CODE
<turbo-stream action="replace" target="messages">
<template>
<div id="message_1">This changes the existing message!</div>
</template>
</turbo-stream>
When we click on Post any message button, the message that appear below that button will get replaced with the message that is mentioned in the app/views/other/post_something.turbo_stream.erb template
There are some cases in an application where JS is needed, therefore to cover those scenarios we require Hotwire JS tool. Hotwire has a JS tool because in some scenarios Turbo-* tools are not sufficient. But as we know that Hotwire is used to reduce the usage of JS in an application, Stimulus considers HTML as the single source of truth. Consider the case where we have to give elements on a page some JavaScript attributes, such as data controller, data-action, and data target. For that, a stimulus controller that can access elements and receive events based on those characteristics will be created.
Make a change in app/views/other/index.html.erb template file in rails application
#CODE
<h1>This is Another page</h1>
<div><%= link_to "Enter to home page", root_path %></div>
<div style="margin-top: 2rem;">
<%= form_with scope: :any, url: post_something_path do |form| %>
<%= form.submit 'Post something' %>
<% end %>
<turbo-frame id="messages">
<div>An empty message</div>
</turbo-frame>
</div>
<div style="margin-top: 2rem;">
<h2>Stimulus</h2>
<div data-controller="hello">
<input data-hello-target="name" type="text">
<button data-action="click->hello#greet">
Greet
</button>
<span data-hello-target="output">
</span>
</div>
</div>
Make changes in the hello_controller.js in path app/JavaScript/controllers and add a stimulus controller in the file, which helps to bring the HTML into life.
#CODE
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "name", "output" ]
greet() {
this.outputTarget.textContent =
`Hello, ${this.nameTarget.value}!`
}
}
Go to your browser after making the changes in the code and click on Enter to other page link which will navigate to the localhost:3000/other/index page there you can see the changes implemented by the stimulus controller that is designed to augment your HTML with just enough behavior to make it more responsive.
With just a little bit of work, Turbo and Stimulus together offer a complete answer for applications that are quick and compelling.
Using Rails 7 Hotwire helps to load the pages at a faster speed and allows you to render templates on the server, where you have access to your whole domain model. It is a productive development experience in ROR, without compromising any of the speed or responsiveness associated with SPA.
We hope you were satisfied with our Rails Hotwire tutorial. Write to us at service@bacancy.com for any query that you want to resolve, or if you want us to share a tutorial on your query.
For more such solutions on RoR, check out our Ruby on Rails Tutorials. We will always strive to amaze you and cater to your needs.
Original article source at: https://www.bacancytechnology.com/
1595098800
Android Interview Questions and Answers from Beginner to Advanced level
DataFlair is committed to provide you all the resources to make you an android professional. We started with android tutorials along with practicals, then we published Real-time android projects along with source code. Now, we come up with frequently asked android interview questions, which will help you in showing expertise in your next interview.
Android – one of the hottest technologies, which is having a bright future. Get ready to crack your next interview with the following android interview questions. These interview questions start with basic and cover deep concepts along with advanced topics.
1. What is Android?
Android is an open-source mobile operating system that is based on the modified versions of Linux kernel. Though it was mainly designed for smartphones, now it is being used for Tablets, Televisions, Smartwatches, and other Android wearables.
2. Who is the inventor of Android Technology?
The inventors of Android Technology are- Andry Rubin, Nick Sears, and Rich Miner.
3. What is the latest version of Android?
The latest version of Android is Android 10.0, known as Android Q. The upcoming major Android release is Android 11, which is the 18th version of Android. [Note: Keep checking the versions, it is as of June 2020.]
4. How many Android versions can you recall right now?
Till now, there are 17 versions of Android, which have their names in alphabetical order. The 18th version of Android is also going to come later this year. The versions of Android are here:
5. Explain the Android Architecture with its components.
This is a popular android developer interview question
Android Architecture consists of 5 components that are-
a. Linux Kernel: It is the foundation of the Android Architecture that resides at the lowest level. It provides the level of abstraction for hardware devices and upper layer components. Linux Kernel also provides various important hardware drivers that act as software interfaces for hardwares like camera, bluetooth, etc.
b. Native Libraries: These are the libraries for Android that are written in C/C++. These libraries are useful to build many core services like ART and HAL. It provides support for core features.
c. Android Runtime: It is an Android Runtime Environment. Android Operating System uses it during the execution of the app. It performs the translation of the application bytecode into the native instructions. The runtime environment of the device then executes these native instructions.
d. Application Framework: Application Framework provides many java classes and interfaces for app development. And it also provides various high-level services. This complete Application framework makes use of Java.
e. Applications: This is the topmost layer of Android Architecture. It provides applications for the end-user, so they can use the android device and compute the tasks.
6. What are the services that the Application framework provides?
The Android application framework has the following key services-
a. Activity Manager: It uses testing and debugging methods.
b. Content provider: It provides the data from application to other layers.
c. Resource Manager: This provides users access to resources.
d. Notification Manager: This gives notification to the users regarding actions taking place in the background.
e. View System: It is the base class for widgets, and it is also responsible for event handling.
7. What are the important features of Linux Kernel?
The important features of the Linux Kernel are as follows:
a. Power Management: Linux Kernel does power management to enhance and improve the battery life of the device.
b. Memory Management: It is useful for the maximum utilization of the available memory of the device.
c. Device Management: It includes managing all the hardware device drivers. It maximizes the utilization of the available resources.
d. Security: It ensures that no application has any such permission that it affects any other application in order to maintain security.
e. Multi-tasking: Multi-tasking provides the users the ease of doing multiple tasks at the same time.
8. What are the building blocks of an Android Application?
This is a popular android interview question for freshers.
The main components of any Android application are- Activity, Services, Content Provider, and Broadcast Receiver. You can understand them as follows:
a. Activity- It is a class that acts as the entry point representing a single screen to the user. It is like a window to show the user interface.
b. Services- Services are the longest-running component that runs in the background.
c. Content Provider- The content provider is an essential component that allows apps to share data between themselves.
d. Broadcast receivers- Broadcast receiver is another most crucial application component. It helps the apps to receive and respond to broadcast messages from the system or some other application.
9. What are the important components of Android Application?
The Components of Android application are listed below:
10. What are the widgets?
Widgets are the variations of Broadcast receivers. They are an important part of home screen customization. They often display some data and also allow users to perform actions on them. Mostly they display the app icon on the screen.
11. Can you name some types of widgets?
Mentioned below are the types of widgets-
a. Informative Widgets: These widgets show some important information. Like, the clock widget or a weather widget.
b. Collective Widgets: They are the collection of some types of elements. For example, a music widget that lets us change, skip, or forward the song.
c. Control Widgets: These widgets help us control the actions within the application through it. Like an email widget that helps check the recent mails.
d. Hybrid Widgets: Hybrid widgets are those that consist of at least two or more types of widgets.
12. What are Intents?
Intents are an important part of Android Applications. They enable communication between components of the same application as well as separate applications. The Intent signals the Android system about a certain event that has occurred.
13. Explain the types of intents briefly?
Intent is of three types that are-
a. Implicit Intents: Implicit intents are those in which there is no description of the component name but only the action.
b. Explicit Intents: In explicit intents, the target component is present by declaring the name of the component.
c. Pending Intents: These are those intents that act as a shield over the Intent objects. It covers the intent objects and grants permission to the external app components to access them.
14. What is a View?
A view is an important building block that helps in designing the user interface of the application. It can be a rectangular box or a circular shape, for example, Text View, Edit Text, Buttons, etc. Views occupy a certain area of the screen, and it is also responsible for event handling. A view is the superclass of all the graphical user interface components.
15. What do you understand by View Group?
It is the subclass of the ViewClass. It gives an invisible container to hold layouts or views. You can understand view groups as special views that are capable of holding other views, that are Child View.
16. What do you understand about Shared Preferences?
It is a simple mechanism for data storage in Android. In this, there is no need to create files, and using APIs, it stores the data in XML files. It stores the data in the pair of key-values. SharedPreferences class lets the user save the values and retrieve them when required. Using SharedPreferences we can save primitive data like- boolean, float, integer, string and long.
17. What is a Notification?
A notification is just like a message that shows up outside the Application UI to provide reminders to the users. They remind the user about a message received, or some other timely information from the app.
18. Give names of Notification types.
There are three types of notifications namely-
a. Toast Notification- This notification is the one that fades away sometime after it pops up.
b. Status Notification- This notification stays till the user takes some action on it.
c. Dialog Notification- This notification is the result of an Active Activity.
19. What are fragments?
A fragment is a part of the complete user interface. These are present in Activity, and an activity can have one or more fragments at the same time. We can reuse a fragment in multiple activities as well.
20. What are the types of fragments?
There are three types of fragments that are: Single Fragment, List Fragment, Fragment Transactions.
21. What are Layout XML files?
Layout XML files contain the structure for the user interface of the application. The XML file also contains various different layouts and views, and they also specify various GUI components that are there in Activity or fragments.
22. What are Resources in Android Application?
The resources in Android Apps defines images, texts, strings, colors, etc. Everything in resources directory is referenced in the source code of the app so that we can use them.
23. Can you develop Android Apps with languages other than Java? If so, name some.
Yes, there are many languages that we can work with, for the development of Android Applications. To name some, I would say Java, Python, C, C++, Kotlin, C#, Corona/LUA.
24. What are the states of the Activity Lifecycle?
Activity lifecycle has the following four stages-
a. Running State: As soon as the activity starts, it is the first state.
b. Paused State: When some other activity starts without closing the previous one, the running activity turns into the Paused state.
c. Resume State: When the activity opens again after being in pause state, it comes into the Resume State.
d. Stopped State: When the user closes the application or stops using it, the activity goes to the Stopped state.
25. What are some methods of Activity?
The methods of Activity are as follows:
26. How can you launch an activity in Android?
We launch an activity using Intents. For this we need to use intent as follows:
27. What is the service lifecycle?
There are two states of a service that are-
a. Started State: This is when the service starts its execution. A Services come in start state only through the startService() method.
b. Bounded State: A service is in the bounded state when it calls the method bindService().
28. What are some methods of Services?
The methods of service are as follows-
29. What are the types of Broadcast?
Broadcasts are of two types that are-
a. Ordered Broadcast: Ordered broadcasts are Synchronous and work in a proper order. It decides the order by using the priority assigned to the broadcasts.
b. Normal Broadcast: These are asynchronous and unordered. They are more efficient as they run unorderly and all at once. But, they lack full utilization of the results.
30. What are useful impotent folders in Android?
The impotent folders in an Android application are-
31. What are the important files for Android Application when working on Android Studio?
This is an important android studio interview question
There are following three files that we need to work on for an application to work-
a. The AndroidManifest.xml file: It has all the information about the application.
b. The MainActivity.java file: It is the app file that actually gets converted to the dalvik executable and runs the application. It is written in java.
c. The Activity_main.xml file: It is the layout file that is available in the res/layout directory. It is another mostly used file while developing the application.
32. Which database do you use for Android Application development?
The database that we use for Android Applications is SQLite. It is because SQLite is lightweight and specially developed for Android Apps. SQLite works the same way as SQL using the same commands.
33. Tell us some features of Android OS.
The best features of Android include-
34. Why did you learn Android development?
Learning Android Studio is a good idea because of the following-
35. What are the different ways of storage supported in Android?
The various storage ways supported in Android are as follows:
36. What are layouts?
Layout is nothing but arrangements of elements on the device screen. These elements can be images, tests, videos, anything. They basically define the structure of the Android user interface to make it user friendly.
37. How many layout types are there?
The type of layouts used in Android Apps are as follows:
38. What is an APK?
An APK stands for Android Package that is a file format of Android Applications. Android OS uses this package for the distribution and installation of the Android Application.
39. What is an Android Manifest file?
The manifest file describes all the essential information about the project application for build tools, Android operating system, and google play. This file is a must for every Android project that we develop, and it is present in the root of the project source set.
#android tutorials #android basic interview questions #android basic questions #android developer interview questions #android interview question and answer #android interview questions #android interview questions for experienced #android interview questions for fresher
1603904400
As of this writing, the market is tough. We’ve been hit hard with a deadly
pandemic that left thousands of people unemployed. It’s layoffs everywhere and the companies are being conservative when it comes to
hiring.
Companies are not willing to hire people with no experience or people who they’ve to train.
Your first job in tech is the toughest, you’re competing
with virtually every new college grad and anyone who completed a boot
camp. I know it can be hard to even land an interview, for someone to
give you a chance to talk and demonstrate you could be valuable
employee.
Now, the chance of you getting an interview totally depends on how your resume compares to the job description. The more relevant it is to the
skills required, the better your chances of getting an interview.
To build your resume, I’d recommend https://thetechresume.com. It’s a nice read to follow the principles when it comes to building a tech resume.
Over the past few months, I’ve been collecting resources like videos,
websites, and taking notes to prepare for coding interviews.
In that process, I made an 8 weeks study guide curated of important data
structure resources to prepare for tech interviews and honestly this
study guide was helpful to me to know what to study every day and in
following a routine for my job search.
If you’re serious about preparing for a tech interview then 8 weeks is the
minimum to be given to prepare thoroughly for a tech interview. I know
there are few who would cram up pools of content in a week or two. But, I
believe that is not a realistic or sensible approach.
Tech interviews can be intense and most companies expect you to solve problems or go through a data structure topic in detail.
Now, My study guide with resources will eat up the entire blog space. So,
Instead of straight-up dumping down the content all together, I racked
my brains on how to deliver the content in the most effective way
possible to ensure the habit of consistency and dedication stays intact
during the interview preparation process.
In this blog post, I would give you what to cover each week. If you’re
interested to know what resources to refer to when covering each topic then I’d recommend subscribing to the newsletter https://thedailycoding.com in which you’ll receive one email daily about the concept and the resources to practice.
If you believe you can find resources to relevant topics on your own then
here’s how you should plan to cover each topic every week.
#coding-interviews #software-development #job-interview #job-search #coding #latest-tech-stories #coding-interview-tips #coding-job-interview-advice
1630743562
FHIR_DB
This is really just a wrapper around Sembast_SQFLite - so all of the heavy lifting was done by Alex Tekartik. I highly recommend that if you have any questions about working with this package that you take a look at Sembast. He's also just a super nice guy, and even answered a question for me when I was deciding which sembast version to use. As usual, ResoCoder also has a good tutorial.
I have an interest in low-resource settings and thus a specific reason to be able to store data offline. To encourage this use, there are a number of other packages I have created based around the data format FHIR. FHIR® is the registered trademark of HL7 and is used with the permission of HL7. Use of the FHIR trademark does not constitute endorsement of this product by HL7.
So, while not absolutely necessary, I highly recommend that you use some sort of interface class. This adds the benefit of more easily handling errors, plus if you change to a different database in the future, you don't have to change the rest of your app, just the interface.
I've used something like this in my projects:
class IFhirDb {
IFhirDb();
final ResourceDao resourceDao = ResourceDao();
Future<Either<DbFailure, Resource>> save(Resource resource) async {
Resource resultResource;
try {
resultResource = await resourceDao.save(resource);
} catch (error) {
return left(DbFailure.unableToSave(error: error.toString()));
}
return right(resultResource);
}
Future<Either<DbFailure, List<Resource>>> returnListOfSingleResourceType(
String resourceType) async {
List<Resource> resultList;
try {
resultList =
await resourceDao.getAllSortedById(resourceType: resourceType);
} catch (error) {
return left(DbFailure.unableToObtainList(error: error.toString()));
}
return right(resultList);
}
Future<Either<DbFailure, List<Resource>>> searchFunction(
String resourceType, String searchString, String reference) async {
List<Resource> resultList;
try {
resultList =
await resourceDao.searchFor(resourceType, searchString, reference);
} catch (error) {
return left(DbFailure.unableToObtainList(error: error.toString()));
}
return right(resultList);
}
}
I like this because in case there's an i/o error or something, it won't crash your app. Then, you can call this interface in your app like the following:
final patient = Patient(
resourceType: 'Patient',
name: [HumanName(text: 'New Patient Name')],
birthDate: Date(DateTime.now()),
);
final saveResult = await IFhirDb().save(patient);
This will save your newly created patient to the locally embedded database.
IMPORTANT: this database will expect that all previously created resources have an id. When you save a resource, it will check to see if that resource type has already been stored. (Each resource type is saved in it's own store in the database). It will then check if there is an ID. If there's no ID, it will create a new one for that resource (along with metadata on version number and creation time). It will save it, and return the resource. If it already has an ID, it will copy the the old version of the resource into a _history store. It will then update the metadata of the new resource and save that version into the appropriate store for that resource. If, for instance, we have a previously created patient:
{
"resourceType": "Patient",
"id": "fhirfli-294057507-6811107",
"meta": {
"versionId": "1",
"lastUpdated": "2020-10-16T19:41:28.054369Z"
},
"name": [
{
"given": ["New"],
"family": "Patient"
}
],
"birthDate": "2020-10-16"
}
And we update the last name to 'Provider'. The above version of the patient will be kept in _history, while in the 'Patient' store in the db, we will have the updated version:
{
"resourceType": "Patient",
"id": "fhirfli-294057507-6811107",
"meta": {
"versionId": "2",
"lastUpdated": "2020-10-16T19:45:07.316698Z"
},
"name": [
{
"given": ["New"],
"family": "Provider"
}
],
"birthDate": "2020-10-16"
}
This way we can keep track of all previous version of all resources (which is obviously important in medicine).
For most of the interactions (saving, deleting, etc), they work the way you'd expect. The only difference is search. Because Sembast is NoSQL, we can search on any of the fields in a resource. If in our interface class, we have the following function:
Future<Either<DbFailure, List<Resource>>> searchFunction(
String resourceType, String searchString, String reference) async {
List<Resource> resultList;
try {
resultList =
await resourceDao.searchFor(resourceType, searchString, reference);
} catch (error) {
return left(DbFailure.unableToObtainList(error: error.toString()));
}
return right(resultList);
}
You can search for all immunizations of a certain patient:
searchFunction(
'Immunization', 'patient.reference', 'Patient/$patientId');
This function will search through all entries in the 'Immunization' store. It will look at all 'patient.reference' fields, and return any that match 'Patient/$patientId'.
The last thing I'll mention is that this is a password protected db, using AES-256 encryption (although it can also use Salsa20). Anytime you use the db, you have the option of using a password for encryption/decryption. Remember, if you setup the database using encryption, you will only be able to access it using that same password. When you're ready to change the password, you will need to call the update password function. If we again assume we created a change password method in our interface, it might look something like this:
class IFhirDb {
IFhirDb();
final ResourceDao resourceDao = ResourceDao();
...
Future<Either<DbFailure, Unit>> updatePassword(String oldPassword, String newPassword) async {
try {
await resourceDao.updatePw(oldPassword, newPassword);
} catch (error) {
return left(DbFailure.unableToUpdatePassword(error: error.toString()));
}
return right(Unit);
}
You don't have to use a password, and in that case, it will save the db file as plain text. If you want to add a password later, it will encrypt it at that time.
After using this for a while in an app, I've realized that it needs to be able to store data apart from just FHIR resources, at least on occasion. For this, I've added a second class for all versions of the database called GeneralDao. This is similar to the ResourceDao, but fewer options. So, in order to save something, it would look like this:
await GeneralDao().save('password', {'new':'map'});
await GeneralDao().save('password', {'new':'map'}, 'key');
The difference between these two options is that the first one will generate a key for the map being stored, while the second will store the map using the key provided. Both will return the key after successfully storing the map.
Other functions available include:
// deletes everything in the general store
await GeneralDao().deleteAllGeneral('password');
// delete specific entry
await GeneralDao().delete('password','key');
// returns map with that key
await GeneralDao().find('password', 'key');
FHIR® is a registered trademark of Health Level Seven International (HL7) and its use does not constitute an endorsement of products by HL7®
Run this command:
With Flutter:
$ flutter pub add fhir_db
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):
dependencies:
fhir_db: ^0.4.3
Alternatively, your editor might support or flutter pub get. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:fhir_db/dstu2.dart';
import 'package:fhir_db/dstu2/fhir_db.dart';
import 'package:fhir_db/dstu2/general_dao.dart';
import 'package:fhir_db/dstu2/resource_dao.dart';
import 'package:fhir_db/encrypt/aes.dart';
import 'package:fhir_db/encrypt/salsa.dart';
import 'package:fhir_db/r4.dart';
import 'package:fhir_db/r4/fhir_db.dart';
import 'package:fhir_db/r4/general_dao.dart';
import 'package:fhir_db/r4/resource_dao.dart';
import 'package:fhir_db/r5.dart';
import 'package:fhir_db/r5/fhir_db.dart';
import 'package:fhir_db/r5/general_dao.dart';
import 'package:fhir_db/r5/resource_dao.dart';
import 'package:fhir_db/stu3.dart';
import 'package:fhir_db/stu3/fhir_db.dart';
import 'package:fhir_db/stu3/general_dao.dart';
import 'package:fhir_db/stu3/resource_dao.dart';
import 'package:fhir/r4.dart';
import 'package:fhir_db/r4.dart';
import 'package:flutter/material.dart';
import 'package:test/test.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final resourceDao = ResourceDao();
// await resourceDao.updatePw('newPw', null);
await resourceDao.deleteAllResources(null);
group('Playing with passwords', () {
test('Playing with Passwords', () async {
final patient = Patient(id: Id('1'));
final saved = await resourceDao.save(null, patient);
await resourceDao.updatePw(null, 'newPw');
final search1 = await resourceDao.find('newPw',
resourceType: R4ResourceType.Patient, id: Id('1'));
expect(saved, search1[0]);
await resourceDao.updatePw('newPw', 'newerPw');
final search2 = await resourceDao.find('newerPw',
resourceType: R4ResourceType.Patient, id: Id('1'));
expect(saved, search2[0]);
await resourceDao.updatePw('newerPw', null);
final search3 = await resourceDao.find(null,
resourceType: R4ResourceType.Patient, id: Id('1'));
expect(saved, search3[0]);
await resourceDao.deleteAllResources(null);
});
});
final id = Id('12345');
group('Saving Things:', () {
test('Save Patient', () async {
final humanName = HumanName(family: 'Atreides', given: ['Duke']);
final patient = Patient(id: id, name: [humanName]);
final saved = await resourceDao.save(null, patient);
expect(saved.id, id);
expect((saved as Patient).name?[0], humanName);
});
test('Save Organization', () async {
final organization = Organization(id: id, name: 'FhirFli');
final saved = await resourceDao.save(null, organization);
expect(saved.id, id);
expect((saved as Organization).name, 'FhirFli');
});
test('Save Observation1', () async {
final observation1 = Observation(
id: Id('obs1'),
code: CodeableConcept(text: 'Observation #1'),
effectiveDateTime: FhirDateTime(DateTime(1981, 09, 18)),
);
final saved = await resourceDao.save(null, observation1);
expect(saved.id, Id('obs1'));
expect((saved as Observation).code.text, 'Observation #1');
});
test('Save Observation1 Again', () async {
final observation1 = Observation(
id: Id('obs1'),
code: CodeableConcept(text: 'Observation #1 - Updated'));
final saved = await resourceDao.save(null, observation1);
expect(saved.id, Id('obs1'));
expect((saved as Observation).code.text, 'Observation #1 - Updated');
expect(saved.meta?.versionId, Id('2'));
});
test('Save Observation2', () async {
final observation2 = Observation(
id: Id('obs2'),
code: CodeableConcept(text: 'Observation #2'),
effectiveDateTime: FhirDateTime(DateTime(1981, 09, 18)),
);
final saved = await resourceDao.save(null, observation2);
expect(saved.id, Id('obs2'));
expect((saved as Observation).code.text, 'Observation #2');
});
test('Save Observation3', () async {
final observation3 = Observation(
id: Id('obs3'),
code: CodeableConcept(text: 'Observation #3'),
effectiveDateTime: FhirDateTime(DateTime(1981, 09, 18)),
);
final saved = await resourceDao.save(null, observation3);
expect(saved.id, Id('obs3'));
expect((saved as Observation).code.text, 'Observation #3');
});
});
group('Finding Things:', () {
test('Find 1st Patient', () async {
final search = await resourceDao.find(null,
resourceType: R4ResourceType.Patient, id: id);
final humanName = HumanName(family: 'Atreides', given: ['Duke']);
expect(search.length, 1);
expect((search[0] as Patient).name?[0], humanName);
});
test('Find 3rd Observation', () async {
final search = await resourceDao.find(null,
resourceType: R4ResourceType.Observation, id: Id('obs3'));
expect(search.length, 1);
expect(search[0].id, Id('obs3'));
expect((search[0] as Observation).code.text, 'Observation #3');
});
test('Find All Observations', () async {
final search = await resourceDao.getResourceType(
null,
resourceTypes: [R4ResourceType.Observation],
);
expect(search.length, 3);
final idList = [];
for (final obs in search) {
idList.add(obs.id.toString());
}
expect(idList.contains('obs1'), true);
expect(idList.contains('obs2'), true);
expect(idList.contains('obs3'), true);
});
test('Find All (non-historical) Resources', () async {
final search = await resourceDao.getAll(null);
expect(search.length, 5);
final patList = search.toList();
final orgList = search.toList();
final obsList = search.toList();
patList.retainWhere(
(resource) => resource.resourceType == R4ResourceType.Patient);
orgList.retainWhere(
(resource) => resource.resourceType == R4ResourceType.Organization);
obsList.retainWhere(
(resource) => resource.resourceType == R4ResourceType.Observation);
expect(patList.length, 1);
expect(orgList.length, 1);
expect(obsList.length, 3);
});
});
group('Deleting Things:', () {
test('Delete 2nd Observation', () async {
await resourceDao.delete(
null, null, R4ResourceType.Observation, Id('obs2'), null, null);
final search = await resourceDao.getResourceType(
null,
resourceTypes: [R4ResourceType.Observation],
);
expect(search.length, 2);
final idList = [];
for (final obs in search) {
idList.add(obs.id.toString());
}
expect(idList.contains('obs1'), true);
expect(idList.contains('obs2'), false);
expect(idList.contains('obs3'), true);
});
test('Delete All Observations', () async {
await resourceDao.deleteSingleType(null,
resourceType: R4ResourceType.Observation);
final search = await resourceDao.getAll(null);
expect(search.length, 2);
final patList = search.toList();
final orgList = search.toList();
patList.retainWhere(
(resource) => resource.resourceType == R4ResourceType.Patient);
orgList.retainWhere(
(resource) => resource.resourceType == R4ResourceType.Organization);
expect(patList.length, 1);
expect(patList.length, 1);
});
test('Delete All Resources', () async {
await resourceDao.deleteAllResources(null);
final search = await resourceDao.getAll(null);
expect(search.length, 0);
});
});
group('Password - Saving Things:', () {
test('Save Patient', () async {
await resourceDao.updatePw(null, 'newPw');
final humanName = HumanName(family: 'Atreides', given: ['Duke']);
final patient = Patient(id: id, name: [humanName]);
final saved = await resourceDao.save('newPw', patient);
expect(saved.id, id);
expect((saved as Patient).name?[0], humanName);
});
test('Save Organization', () async {
final organization = Organization(id: id, name: 'FhirFli');
final saved = await resourceDao.save('newPw', organization);
expect(saved.id, id);
expect((saved as Organization).name, 'FhirFli');
});
test('Save Observation1', () async {
final observation1 = Observation(
id: Id('obs1'),
code: CodeableConcept(text: 'Observation #1'),
effectiveDateTime: FhirDateTime(DateTime(1981, 09, 18)),
);
final saved = await resourceDao.save('newPw', observation1);
expect(saved.id, Id('obs1'));
expect((saved as Observation).code.text, 'Observation #1');
});
test('Save Observation1 Again', () async {
final observation1 = Observation(
id: Id('obs1'),
code: CodeableConcept(text: 'Observation #1 - Updated'));
final saved = await resourceDao.save('newPw', observation1);
expect(saved.id, Id('obs1'));
expect((saved as Observation).code.text, 'Observation #1 - Updated');
expect(saved.meta?.versionId, Id('2'));
});
test('Save Observation2', () async {
final observation2 = Observation(
id: Id('obs2'),
code: CodeableConcept(text: 'Observation #2'),
effectiveDateTime: FhirDateTime(DateTime(1981, 09, 18)),
);
final saved = await resourceDao.save('newPw', observation2);
expect(saved.id, Id('obs2'));
expect((saved as Observation).code.text, 'Observation #2');
});
test('Save Observation3', () async {
final observation3 = Observation(
id: Id('obs3'),
code: CodeableConcept(text: 'Observation #3'),
effectiveDateTime: FhirDateTime(DateTime(1981, 09, 18)),
);
final saved = await resourceDao.save('newPw', observation3);
expect(saved.id, Id('obs3'));
expect((saved as Observation).code.text, 'Observation #3');
});
});
group('Password - Finding Things:', () {
test('Find 1st Patient', () async {
final search = await resourceDao.find('newPw',
resourceType: R4ResourceType.Patient, id: id);
final humanName = HumanName(family: 'Atreides', given: ['Duke']);
expect(search.length, 1);
expect((search[0] as Patient).name?[0], humanName);
});
test('Find 3rd Observation', () async {
final search = await resourceDao.find('newPw',
resourceType: R4ResourceType.Observation, id: Id('obs3'));
expect(search.length, 1);
expect(search[0].id, Id('obs3'));
expect((search[0] as Observation).code.text, 'Observation #3');
});
test('Find All Observations', () async {
final search = await resourceDao.getResourceType(
'newPw',
resourceTypes: [R4ResourceType.Observation],
);
expect(search.length, 3);
final idList = [];
for (final obs in search) {
idList.add(obs.id.toString());
}
expect(idList.contains('obs1'), true);
expect(idList.contains('obs2'), true);
expect(idList.contains('obs3'), true);
});
test('Find All (non-historical) Resources', () async {
final search = await resourceDao.getAll('newPw');
expect(search.length, 5);
final patList = search.toList();
final orgList = search.toList();
final obsList = search.toList();
patList.retainWhere(
(resource) => resource.resourceType == R4ResourceType.Patient);
orgList.retainWhere(
(resource) => resource.resourceType == R4ResourceType.Organization);
obsList.retainWhere(
(resource) => resource.resourceType == R4ResourceType.Observation);
expect(patList.length, 1);
expect(orgList.length, 1);
expect(obsList.length, 3);
});
});
group('Password - Deleting Things:', () {
test('Delete 2nd Observation', () async {
await resourceDao.delete(
'newPw', null, R4ResourceType.Observation, Id('obs2'), null, null);
final search = await resourceDao.getResourceType(
'newPw',
resourceTypes: [R4ResourceType.Observation],
);
expect(search.length, 2);
final idList = [];
for (final obs in search) {
idList.add(obs.id.toString());
}
expect(idList.contains('obs1'), true);
expect(idList.contains('obs2'), false);
expect(idList.contains('obs3'), true);
});
test('Delete All Observations', () async {
await resourceDao.deleteSingleType('newPw',
resourceType: R4ResourceType.Observation);
final search = await resourceDao.getAll('newPw');
expect(search.length, 2);
final patList = search.toList();
final orgList = search.toList();
patList.retainWhere(
(resource) => resource.resourceType == R4ResourceType.Patient);
orgList.retainWhere(
(resource) => resource.resourceType == R4ResourceType.Organization);
expect(patList.length, 1);
expect(patList.length, 1);
});
test('Delete All Resources', () async {
await resourceDao.deleteAllResources('newPw');
final search = await resourceDao.getAll('newPw');
expect(search.length, 0);
await resourceDao.updatePw('newPw', null);
});
});
}
Download Details:
Author: MayJuun
Source Code: https://github.com/MayJuun/fhir/tree/main/fhir_db