Adding a class alias at boot time in Laravel
History / Edit / PDF / EPUB / BIB / 2 min read (~281 words)I make extensive use of Laravel Debugbar to track performance of parts of my application. I sprinkle calls to Debugbar::startMeasure
and Debugbar::stopMeasure
to track the duration of certain segments of my code. However, when this code goes into production, this dependency isn't present. This cause the code to break since it cannot find Debugbar
anymore.
To solve this issue, I thought I would create a dummy Debugbar
class and have it added as an alias, so that any code depending on Debugbar
would still work, but end up as a "no operation". I found the article Dynamic class aliases in package which introduced the necessary piece of information to accomplish this.
<?php
use Illuminate\Foundation\AliasLoader;
use My\SuperPackage\FooBar;
class ServiceProvider extends \Illuminate\Support\ServiceProvider
{
public function register()
{
$this->app->booting(function() {
$loader = AliasLoader::getInstance();
$loader->alias('FooBar', FooBar::class);
});
}
}
In my desired use case, I simply implemented the following changes:
In app/Providers/DebugbarServiceProvider.php
(a new file)
<?php
namespace App\Providers;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\ServiceProvider;
class DebugbarServiceProvider extends ServiceProvider
{
public function register()
{
if (!class_exists('Debugbar')) {
$loader = AliasLoader::getInstance();
$loader->alias('Debugbar', NullDebugbar::class);
}
}
}
class NullDebugbar
{
public static function __callStatic(string $name, array $arguments)
{
// Do nothing
}
}
In app/config/app.php
// under the 'providers' key, add
'providers' => [
[...]
// This will take care of loading the service provider defined above
App\Providers\DebugbarServiceProvider::class,
],
With those two changes, it is now possible to make use of Debugbar
in most places and have it work even without the Laravel Debugbar dependency installed.
Why do developers fight over code style?
We are creatures of habits. We like when our code looks like we would expect it to look and not some completely different style. When the style is too different, then it creates cognitive load, which means that we're spending more energy than we would if the code looked the way we like it. Since we're machines that attempt to minimize the amount of energy we spend, we see code that is not styled our way as a bad investment of our energy and that it would either be better to reformat the code our way (minimizing our energy expenditure in the future) or simply to start from scratch.
As human beings, we're able to adapt. Adapting generally requires more energy than simply using the skills we already have, and we prefer to avoid having to adapt. Thus we fight with others so that they do the effort of adapting instead of us. We see fighting as being more effective than adapting. It may be an effective approach when no existing rules exist, however, in many businesses, code standards have been established, which means that if you are a new employee, you will have to adapt to those standards. You could always try to bring back the discussion of updating the code style, but if the standards have been established a long time ago, this effort is likely to be futile.
As such, even though adapting requires more of our energy, we should make that sacrifice upfront and use it on more important things, such as defining what tasks are important and which ones should be done first.
Run your program with python -m cProfile -o profile.cprofile my-script.py
Install snakeviz
(uv pip install snakeviz
) to visualize the generated profile.
snakeviz profile.cprofile
Alternative approach
Install pyprof2calltree
to convert the cprofile to a kcachegrind compatible profile.
pyprof2calltree -i profile.cprofile -o callgrind.profile.cprofile
- Read
package.json
to discover what packages VS Code depends on - Observe the root directory structure, and more specifically the
extensions
andsrc
directories which contain the bulk of the source code- A lot of the code in the
extensions
directory appears to be dedicated to programming language support- The remainder of the extensions seem to provide functionality for things that aren't "core" to vscode, such as
configuration-editing
,emmet
,extension-editing
and some color themes
- The remainder of the extensions seem to provide functionality for things that aren't "core" to vscode, such as
- A lot of the code in the
- If you look at the
.vscode/launch.json
, you will find all the tasks that can be executed from within VS Code debugger. One task of interest isLaunch VS Code
which will take care of launching VS Code for us so that we may debug it- In this file you will also discover that it runs
${workspaceFolder}/scripts/code.bat
, which is the next script we'll take a look at
- In this file you will also discover that it runs
- In
./scripts/code.bat
, we discover that this script will runyarn
if thenode_modules
directory is missing, download the electron binaries if necessary and callgulp compile
if theout
directory is missing, then finally start the electron/vs code binary in the.build/electron
directory - We then start to look for common entry points file such as
index.ts/js
ormain.ts/js
, for which we find a match in thesrc
directory - We take a quick look around, trying to find where electron is likely to be instantiated... There's a lot of code in
src/main.js
that would be better elsewhere to make it easier to navigate this file - Close to the bottom of the file we discover the code we are interested in as a call to
app.once('ready', ...)
- Once the app is ready, we want to call
src/bootstrap-amd
and passvs/code/electron-main/main
as our entry point (per the signature of the exported function in./src/bootstrap-amd
)- Here we can go to two places, either
src/bootstrap-amd
orsrc/vs/code/electron-main/main
- We take a quick peek at both files and we can quickly tell that
src/bootstrap-amd
is used mainly to loadsrc/vs/code/electron-main/main
which is the file we're going to be interested in
- We take a quick peek at both files and we can quickly tell that
- Here we can go to two places, either
- Once the app is ready, we want to call
- Once again, we quickly look around
src/vs/code/electron-main/main
and find that the main logic is at the bottom of the file - First the command line arguments are parsed
- Then services are bootstrapped/instantiated
- Finally the
CodeApplication
is started up - This leads us to look into
src/vs/code/electron-main/app.ts
- As the file is quite large, we start by skimming through it, looking at the available methods on the
CodeApplication
class as well as its properties - Looking at the constructor, we can see that a lot of objects are given to it. We also observe the use of the @... syntax (those are decorators)
- In this case (and for most constructors), this is how VS Code does service (dependencies) injection
-
One will also notice that most, if not all parameters have a visibility assigned to it. What this does is that it will create an associated property in the class as well as assigning the parameter value to this property in the constructor. Thus, instead of writing
class AnotherClass { private someClass: SomeClass; constructor(someClass: SomeClass) { this.someClass = someClass; } }
you simply write
class AnotherClass { constructor(private someClass: SomeClass) { } }
- Upon its creation, the
CodeApplication
class will register various event listeners on the electron app object - If we remember, in
src/vs/code/electron-main/main
, after theCodeApplication
object is instantiated, we callstartup()
on it. So, we want to take a look at what that method does - Without knowing too much about the VS Code source, it appears that we are instantiating an IPC server (inter-process communication) and then the shared process
- After that is done, we initialize some more services in
CodeApplication::initServices
, such as the update service (which I guess takes care of checking for VS Code updates) and the telemetry (data about VS Code feature usage) - We finally get to the point where we're about to open a window in
CodeApplication::openFirstWindow
!- This leads us to go read the class
WindowsManager
insrc/vs/code/electron-main/windows.ts
. Once again, this file is pretty large, so we want to skim it to see what it contains (functions, classes, properties, methods)
- This leads us to go read the class
- There are a few large classes in
src/vs/code/electron-main/windows.ts
that I'd want to extract to make the file smaller and simpler (less cognitive load). However, the issue is that those classes are not declared as exported, and thus are only available in the local file. It would be possible to move these classes to other files and import them, but by doing so it would also "communicate" that others can use it, which is what having the classes as not exported prevents, at the cost of making single files larger and harder to comprehend - We know that the constructor is first called, then from
CodeApplication::openFirstWindow
, we see thatWindowsManager::ready
andWindowsManager::open
are both called.- In the
constructor
we instantiate theDialogs
class (takes care of open/save dialog windows) and theWorkspacesManager
class (takes care of workspace management, such as open/save) - In
ready
event listeners are registered - In
open
there is a lot of logic associated with the window finally opening
- In the
- If you start VS Code using the debug feature, you will not be able to open the Chrome DevTools (at this moment, 2018-05-26) because only 1 process is allowed to attach to the Chrome DevTools instance, and that process is the VS Code editor that started the debugged VS Code instance
Today I want to find out how VS Code restores a windows sessions when you start it. Apparently, if you run it as code .
, it will not restore the same set of windows than if you called it simply with code
.
- In
src/vs/code/electron-main/launch.ts
, theLaunchService::startOpenWindow
appears to implement logic based on how many arguments were given. In all cases, we end up doing a call to theIWindowsMainService::open
method.- Note that in both cases, the path we're opening is within the
args
variable, which is passed to thecli
property of theIOpenConfiguration
object.
- Note that in both cases, the path we're opening is within the
- The implementation of
IWindowsMainService
we are interested in lives insrc/vs/code/electron-main/windows.ts
. - In the
WindowsManager::open
method, we rapidly discover that the windows that will be opened will be retrieved inWindowsManager::getPathsToOpen
. In there, we can observe that the windows that will be opened depend on whether something was passed from the API, we forced an empty window, we're extracting paths from the cli or we should restore from the previous session.- If we arrive at this last case, we can see that the logic is to call
WindowsManager::doGetWindowsFromLastSession
, which is pretty self-explanatory, and will retrieve the previous set of windows from the last session. This is what happens when you startcode
usingcode
- In the case where we pass a path, this path is in
openConfig.cli._
. In this case, the windows that were previously opened, and part ofthis.windowsState.openedWindows
(wherethis
is aWindowsManager
object)- Here we wonder how the
windowsState.openedWindows
state gets restored on VS Code start. To figure that out, we start at theWindowsManager.constructor
method. There we findthis.windowsState = this.stateService.getItem<IWindowsState>(WindowsManager.windowsStateStorageKey) || { openedWindows: [] };
, which states to use get aIWindowState
object from thestateService
if one exists or to create an object with no opened windows. If we assume that this windows state is the same regardless of how we start VS Code, then it is not there that the difference in opened windows will occur.
- Here we wonder how the
- If we arrive at this last case, we can see that the logic is to call
Identity and access management
History / Edit / PDF / EPUB / BIB / 3 min read (~470 words)AWS has the concept of groups, users, roles and policies.
A group is a list of users to which are assigned policies (permissions).
A useful feature they also provide is the Access Advisor, which allows administrators to observe when certain policies are being used and by which users.
To keep things simple, groups cannot be nested into other groups.
Users represent an entity that has access to the AWS platform. They may access AWS either programmatically and/or through the console to administer the account.
During the using creation process, the creator has the ability to assign the user to a group, copy permissions from an existing user or attach policies to the user.
Upon creation of a user with programmatic access, the user is generally given an access key with its corresponding secret.
Roles are very similar to groups conceptually, but instead of being "persistent", they are temporary. While you will generally assign one or many groups to a user, roles are not assigned to a user (or vice-versa, a user is not assigned one or many roles) but given based on certain criteria. A user takes a given role and only receives this role's policies/permissions until he returns to his own identity.
A policy is a set of rules that determines the permissions given to a user, group or role. It has a name, description and a policy document, which describes the permissions.
The policy document is a JSON formatted object which contains a version and a list of statements. Each statement has an effect (Allow/Deny), a list of actions and a list of resources to which it applies.
You can learn more about AWS policies evaluation logic. Their IAM Policy Reference might also prove useful in understanding the various bits that compose the policy document.
Within AWS, each service has a unique lowercase identifier1. Within each of these services, a list of actions exists, which can be granted (or denied) to an identity. Examples of actions are:
*
: Allowed to use all actions under all servicess3:*
: Allowed to use all actions under thes3
services3:GetObject
: Allowed to use theGetObject
action under thes3
service
Resources represent entities within their given service. For example, arn:aws:s3:::some-bucket/*
represents the content under the some-bucket
bucket in the s3
service.
Format : arn:$partition:$service:$region:$account-id:$resource
2