áá±áž áá¬á! áá±á¬ááºážáá«ážáá²á· áá¬áá¬ááŒááºáá»ááºááᯠáááºážáá²á·á¡á¬áá¯á¶ááᯠáá«áááºááŒáááºá
Oxymoron ááá¯ááºáá°ážááá¯á· ááááá±ážáá«áááº"
Object-oriented ááŸáá·áº functional programming paradigms áá»á¬ážááẠáá áºáá¯ááŸáá·áºáá áºáᯠááœá²ááœá²áá±áá¯á¶ááá±á¬áºáááºáž ááŸá áºáá¯áá¯á¶ážááᯠPowershell ááœáẠá¡áá®á¡áá»áŸ áá¶á·ááá¯ážáá¬ážáááºá áááá¯ááááºážáááºážáá¬áá¬á áá¬ážá¡á¬ážáá¯á¶ážáá®ážáá«ážááœáẠá¡áá¯ááºáá¯ááºááá¯ááºáááºááŒá áºá á± áááŸááááºááŒá áºá á± ááá¯ážáá»á²á·á¡áááº-áááºááá¯áž áá±á«ááºážá ááºááŸá¯á¡ááœáẠá¡áá±á¬ááºá¡áá°áá á¹á ááºážáá»á¬áž ááŸááááºá á¡áá±á¬ááºá¡áŠáá»á¬ážááŸáá·áº ááŸááºáááºážáá»á¬ážáá²á·ááá¯á· á¡áááºážáá»á¬ážááẠáá»ááºážáááºááŸá¯áá áºáá¯áá¬ááŒá áºáááºá áá»áœááºá¯ááºááá¯á·ááẠáá»áœááºá¯ááºááá¯á·á Classes áá»á¬ážááᯠáá¬áááºáá»á¬ážááŸáá·áº áááºááá¯ážáá»á¬ážááᯠáá±á«ááºážá ááºá¡áá¯á¶ážááŒá¯ááŒááºážááᯠááá·áºáááºáá¬ážááŒá®áž á¡ááœá±áááºáá¶ááŒááºážá á¡ááœááºáá°ááŒááºáž ááá¯á·ááá¯áẠááŒá±á¬ááºážáá²ááŒááºážáá²á·ááá¯á·áá±á¬ áá±ážáá¶áá±á¬ á¡áá¬ááá¹áá¯ááᯠáŠážáááºááá·áº áááá¯ááááºáá±ážááœá²ááŒááºážááá¯ááºáᬠá¡áá°á¡ááá»á¬ážááᯠááŸá±á¬ááºááŒááºáá«áá áá»áœááºá¯ááºááá¯á·á áá¯ááºááᯠáááŸá¯ááºááœá±ážá á±áá² áááºážááá¯á·á á¡áá»áá¯ážáá»á±ážáá°ážáá»á¬ážááᯠá¡áá¯á¶ážáá»ááá¯ááºáá«áááºá ááá¯á·á¡ááŒááºá áááŒá±á¬ááºážáá²ááá¯ááºáá±á¬ á¡áá»áá¯ážá¡á á¬ážááŒá±á¬ááºážáá²ááŒááºážáááºážáááºážáá»á¬ážááᯠáá±á«ááºážááá·áºááŒááºážááŒáá·áºá áá»áœááºá¯ááºááá¯á·ááẠáá»áœááºá¯ááºááá¯á·ááá¯ááºáá±á¬ááºááŸá¯áá¯ááºááᯠClasses ááŒáá·áº ááŒáœááºáá á±ááá¯ááºáá«áááºá
ááá¹á áááºáá»ááºáááºá
Castes ááẠPowershell ááœáẠá¡á áœááºážáááºáá¯á¶áž á¡ááºá¹áá«áááºáá»á¬ážáá²á០áá áºáá¯ááŒá áºáááºá áááºáááºááá¯ážáá áºáá¯ááᯠáá¬á áºáá¯ááºáá±á¬á¡áá«á áááºááẠááá·áºá¡ááá®áá±ážááŸááºážááœáẠáááºáááºážáá»ááºá áá±á«ááºážááá·áºáá¬ážáá±á¬ ááœááºááá¯ááºáá±á¬áááŠážááŒá¯ááŒááºážááŸáá·áº ááá¬ážáááºá¡áááºááŒá¯ááá¯ááºááŸá¯á¡áá±á«áº á¡á¬ážááá¯ážáá±áá«áááºá á¥ááá¬á¡á¬ážááŒáá·áºá [xml] ááœáẠstring áá áºáá¯ááᯠáá¬á áºáá¯ááºááŒááºážááŒáá·áº áááºážááᯠparser code ááŸáááá·áº run ááŒá®áž ááŒá®ážááŒáá·áºá á¯á¶áá±á¬ xml tree ááᯠáá¯ááºáá±ážáááá·áºáááºá áá»áœááºá¯ááºááá¯á·ááẠáá°áá®áá±á¬áááºááœááºáá»ááºá¡ááœáẠáá»áœááºá¯ááºááá¯á·ááá¯ááºááœáẠClasses ááá¯áá¯á¶ážááá¯ááºáááºá
hashables áá»á¬ážááᯠáá¬á áºáá«á
ááá·áºááœáẠconstructor áááŸááá±ážáá«áá ááá·áº class type ááá¯á· hashable áá áºáá¯ááᯠáá¬á áºáá¯ááºááŒááºážááŒáá·áº áá áºáá¯ááŸááá«áá² áááºáááºáá¯ááºáá±á¬ááºááá¯ááºáááºá á€áá¯á¶á á¶ááá¯á¡ááŒáá·áºá¡áá¡áá¯á¶ážáá»ááẠvalidation attribute áá»á¬ážááá¯áá¯á¶ážáááºááá±á·áá«ááŸáá·áºá áá áºáá»áááºáááºážááŸá¬áááºá áá»áœááºá¯ááºááá¯á·ááẠááá¯ááá¯áááºááŸáá¯ááºážáá±á¬ áááŠážá¡á ááŒá¯ááŒááºážááŸáá·áº á¡áááºááŒá¯ááŒááºážááá¯ááºáᬠáá¯áá¹áááá±áááá¯ááẠáá¯ááºáá±á¬ááºááẠá¡áááºážá ááá¯ááºááá·áºáá¬ážáá±á¬ áá¯ááºááá¹áááá»á¬ážááᯠá¡áá¯á¶ážááŒá¯ááá¯ááºáááºá
class Cluster {
[ValidatePattern("^[A-z]+$")]
[string] $Service
[ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
[string] $FlightingRing
[ValidateSet("EastUS", "WestUS", "NorthEurope")]
[string] $Region
[ValidateRange(0, 255)]
[int] $Index
}
[Cluster]@{
Service = "MyService"
FlightingRing = "PROD"
Region = "EastUS"
Index = 2
}
ááá¯á·á¡ááŒáẠáá¯á¶ááœááºážáá¯ááºááŒááºážááẠááá·áºááŸááºážáá±á¬á¡ááœááºááá¯áááŸáááẠáá°áá®áá±ážáááºá ဠhashtables áá»á¬ážááᯠá¡áááºážáá áºáá¯ááá¯á· ááááá¯á¶ážáá»ááŒáá«á áááºáááŸáááá·áºá¡áá¬ááŸáá·áº Cluster hashtable array á output ááᯠFormat-Table ááá¯á· ááŸáá¯ááºážááŸááºáá«á á¡áááºážáá áºáá¯á áá¯ááºááá¹áááá»á¬ážááẠáááºážááá¯á·ááᯠááá¯áá±áá¬ááœáẠáááºááŸááºááá·áº á¡á á®á¡á á¥áºááœáẠá¡ááŒá²áá±á¬áºááŒáá¬ážáá«áááºá ááááºáá»á¬ážááœáẠáááºáááŒááºááá¯áá±á¬ áá¯ááºááá¹áááá»á¬áž á¡á¬ážáá¯á¶ážááŸá±á·ááœáẠáá»áŸáá¯á·ááŸááºáá±á¬á·áá»ááºá á¬áá¯á¶ážááᯠááá·áºááẠááá±á·áá«ááŸáá·áºá
á¡áááá¹áá«ááºáá±á¬áºáááºá
á¡áááºá ááá·áºááœáẠá¡ááŒááºážá¡áá¯á¶áá áºáá¯áá«ááŸááá±á¬ constructor ááŸááá«áá ááá·áº class type ááá¯á· áááºááá¯ážáá áºáá¯áá»ááŒááºážááẠááá·áº class á instance áá áºáá¯ááᯠá¡á ááŒá¯ááá¯ááºá á±ááá·áº value ááᯠááá·áº constructor ááá¯á·áá±ážááá¯á·áááºááŒá áºáá«áááºá
class Cluster {
[ValidatePattern("^[A-z]+$")]
[string] $Service
[ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
[string] $FlightingRing
[ValidateSet("EastUS", "WestUS", "NorthEurope")]
[string] $Region
[ValidateRange(0, 255)]
[int] $Index
Cluster([string] $id) {
$this.Service, $this.FlightingRing, $this.Region, $this.Index = $id -split "-"
}
}
[Cluster]"MyService-PROD-EastUS-2"
ááá¯ááºážááá¯á· áá¬á áº
string interpolation ááá¯á¡áá¯á¶ážááŒá¯ááŒááºážáá²á·ááá¯á·áá±á¬ á¡áá¬ááá¹áá¯á string ááá¯ááºá á¬ážááŒá¯ááŸá¯áá±á¬ááºááœááºááŸá áá¯áá¹áááá±áááᯠáááºááŸááºááẠ[string] ToString() class method ááᯠáááºáá±ážááá¯ááºáááºá
class Cluster {
[ValidatePattern("^[A-z]+$")]
[string] $Service
[ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
[string] $FlightingRing
[ValidateSet("EastUS", "WestUS", "NorthEurope")]
[string] $Region
[ValidateRange(0, 255)]
[int] $Index
[string] ToString() {
return $this.Service, $this.FlightingRing, $this.Region, $this.Index -join "-"
}
}
$cluster = [Cluster]@{
Service = "MyService"
FlightingRing = "PROD"
Region = "EastUS"
Index = 2
}
Write-Host "We just created a model for '$cluster'"
á¡ááœá²ááá¯áẠáá¬áááá»á¬ážááᯠáá¬á áºá áº
Cast ááẠáá±ážáááºážáá±á¬ deserialization ááá¯ááœáá·áºááŒá¯áááºá Cluster ááŸá áá»áœááºá¯ááºááá¯á·á áááºááŸááºáá»ááºáá»á¬ážááŸáá·áº áááá¯ááºáá®áá«á á¡á±á¬ááºáá«ááá°áá¬áá»á¬ážááẠáá»ááºááœá¬ážáá«áááºá
# ÐалОЎаÑÐžÑ ÑеÑОалОзПваММÑÑ
ЎаММÑÑ
[Cluster]$cluster = Get-Content "./my-cluster.json" | ConvertFrom-Json
[Cluster[]]$clusters = Import-Csv "./my-clusters.csv"
ááá·áºáá¯ááºáá±á¬ááºáá»ááºáá¯ááºááœáẠáá¬á áºáá»á¬áž
Functional programs áá»á¬ážááẠáá±áá¬ááœá²á·á ááºážáá¯á¶áá»á¬ážááᯠáŠážá áœá¬áááºááŸááºááŒááŒá®ážá ááá¯á·áá±á¬áẠáááŒá±á¬ááºážáá²ááá¯ááºáá±á¬ áá±áá¬ááœá²á·á ááºážáá¯á¶áá»á¬ážáá±á«áºááœáẠá¡ááœááºááŒá±á¬ááºážááŸá¯á¡á á®á¡á á¥áºá¡ááŒá Ạáááá¯ááááºááᯠá¡áá±á¬ááºá¡áááºáá±á¬áºáá«á ááá·áºáá»ááºáááºáááºááŒááºáá°ááá»ááºáá»á¬ážááŸááá±áá±á¬áºáááºážá á¡áááºážáá»á¬ážááẠááŒá±á¬ááºážáá²ááŒááºážáááºážáááºážáá»á¬ážááᯠááá¯ááºááá·áºááŒááºážááŒáá·áº áá¯ááºáá±á¬ááºááá¯ááºáá±á¬ áá¯ááºáá»á¬ážááᯠá¡ááŸááºáááẠáá±ážááá¯ááºá á±ááẠáá°áá®áá±ážáá«áááºá
áá»áœááºáá±á¬áºáá±ážáá±áá²á· Powershell á á¡áá¯ááºáá¯ááºááá¯ááºáá«ááá¬ážá
C# ááá¯á·ááá¯áẠá¡áá¬ážáá°áá±á¬ááºáá¶ááŸáá¬áá±á¬ áá°á¡áá»á¬ážá¡ááŒá¬ážááẠC# ááŸáá·áºáááºáá°áá±á¬ Powershell ááá¯áá±ážáá¬ážáá±ááŒáááºá ááá¯ááá¯á·áá¯ááºáá±á¬ááºááŒááºážááŒáá·áº áááºááẠáá¯ááºáá±á¬ááºáá»ááºááá¯ááºáᬠáááá¯ááááºážáááºážá¡áá°á¡ááá»á¬ážááᯠá¡áá¯á¶ážááŒá¯ááŒááºážá០áá±ážááœá¬ááœá¬ážáᬠPowershell ááŸá á¡áá¬ááá¹áá¯-áááºáá±á¬ áááá¯ááááºážáááºážááá¯á· ááŒá®ážááŒá®ážáá¬ážáá¬áž áá áºááŒá¯ááºáá±ááŒááºáž ááá¯á·ááá¯áẠáá¯ááºáá±á¬ááºáá»ááºááá¯ááºáᬠáááá¯ááááºážáááºážá¡ááŒá±á¬ááºáž ááá¯ááá¯áá±á·áá¬ááŒááºážá០á¡áá»áá¯ážááŸáááá¯ááºáááºááŒá áºáááºá
á¡áááºá áááºááẠááá¯ááºááá¯ááºážáá»á¬áž (|)á Where-Objectá ForEach-Objectá Select-Objectá Group-Objectá Sort-Object á áááºááŒáá·áº ááá¯ááºááá¯ááºážáá»á¬ážááᯠá¡áá¯á¶ážááŒá¯á áááŒá±á¬ááºážáá²ááá¯ááºáá±á¬ áá±áá¬ááᯠá¡ááœááºááŒá±á¬ááºážááŒááºážá¡áá±á«áº ááŒá®ážááŒá®ážáá¬ážáá¬áž á¡á¬ážááá¯ážáá«á - ááá·áºááœáẠááá¯ááá¯áá¯ááºáá±á¬ááºááá¯ááºáá±á¬ áá¯á¶á á¶ááŸáááŒá®áž Powershell ááᯠá¡áá¯á¶ážááŒá¯ááŒááºážááŒáá·áº áááºááẠá¡áá»áá¯ážáá»á±ážáá°ážáááŸááááºááŒá áºáááºá functional style ááŒáá·áºá¡áááºážáá»á¬ážá
á¡áááºážáá»á¬ážááᯠá¡áá¯á¶ážáá»ááŒááºáž
Castes ááẠá¡ááŒá¬ážá¡áá¬ážá¡ááá¯áá áºáá¯ááᯠá¡áá¯á¶ážááŒá¯áá±á¬áºáááºážá áááºážááá¯á·ááẠdomain ááŸá áºáá¯ááŒá¬ážááœáẠáá¯á¶áá±á¬áºááŒááºážáá»áŸáá¬ááŒá áºáááºá ááá¯ááºááá¯ááºážááœáẠForEach-Object ááᯠá¡áá¯á¶ážááŒá¯á áááºááá¯ážáá»á¬áž áááºážáá»ááºážááŒáááá¯ááºáááºá
á¡á±á¬ááºáá±á¬áºááŒáá« á¥ááá¬ááœááºá Node constructor ááẠDatum ááᯠáá¬á áºááá¯ááºážááœáẠáá¯ááºáá±á¬ááºááŒá®áž áááºážááẠáá»áœááºá¯ááºááá¯á·á¡á¬áž áá»áŸááá±á¬áá¯ááºááá¬áááᯠááá±ážááá¯ááºá á±ááẠá¡ááœáá·áºá¡áá±ážáá±ážáááºá ááááºá¡áá±ááŒáá·áºá áá»áœááºá¯ááºááá¯á·á ááá¯ááºááá¯ááºážááẠáá±áá¬ááœá²ááŒááºážá áááºááŒá¬ááŸá¯ááŸáá·áº ááá¬ážáááºááŸá¯ááᯠááá¯á áá¯ááºáá±áá»áááºááœáẠáá»áœááºá¯ááºááá¯á·áá¡áááºážáá»á¬ážááẠáá±áá¬ááœá²ááŒááºážá áááºááŒá¬ááŸá¯ááŸáá·áº ááá¬ážáááºááŸá¯ááᯠááá¯á áá¯ááºáá±áá»áááºááœáẠááŒá±ááŒá¬áá±áá¬áá±ážááŒááºážááŒááºážááŸáá·áº á á¯á ááºážááŸá¯á¡áá±á«áº á¡á¬áá¯á¶á áá¯ááºáá«áááºá
# ÐÑÐžÐŒÐµÑ ÐºÐŸÐŒÐ±ÐžÐœÐžÑÐŸÐ²Ð°ÐœÐžÑ ÐºÐ»Ð°ÑÑПв Ñ ÐºÐŸÐœÐ²ÐµÐ¹ÐµÑаЌО ÐŽÐ»Ñ separation of concerns в кПМвейеÑаÑ
class Node {
[ValidateLength(3, 7)]
[string] $Name
[ValidateSet("INT", "PPE", "PROD")]
[string] $FlightingRing
[ValidateSet("EastUS", "WestUS", "NorthEurope", "WestEurope")]
[string] $Region
Node([string] $Name) {
$Name -match "([a-z]+)(INT|PPE|PROD)([a-z]+)"
$_, $this.Service, $this.FlightingRing, $this.Region = $Matches
$this.Name = $Name
}
}
class Datum {
[string] $Name
[int] $Value
[Node] $Computer
[int] Severity() {
$this.Name -match "[0-9]+$"
return $Matches[0]
}
}
Write-Host "Urgent Security Audit Issues:"
Import-Csv "./audit-results.csv" `
| ForEach-Object {[Datum]$_} `
| Where-Object Value -gt 0 `
| Group-Object {$_.Severity()} `
| Where-Object Name -lt 2 `
| ForEach-Object Group `
| ForEach-Object Computer `
| Where-Object FlightingRing -eq "PROD" `
| Sort-Object Name, Region -Unique
ááŒááºáááºá¡áá¯á¶ážááŒá¯áááºá¡ááœáẠáá¯ááºááá¯ážááŸá¯á¡áááºáž
áááºááá±á¬ááºáá±á¬ááºážáᬠáá¬ááŸáááŸááá«áá°ážá
áá¶ááá±á¬ááºážá áœá¬ááŒáá·áºá áá¯ááºáá±á¬ááºáá»ááºáá»á¬áž ááá¯á·ááá¯áẠááááºážááŸááºáá»á¬ážáá²á·ááá¯á· áá°áá®áá±á¬áááºážááŒáá·áº module áá»á¬ážá០á¡áááºážáá»á¬ážááᯠáá¯ááºáá°ááááá«á áá«áá±ááá·áº ááá»áá¯á·ááŸáá·áºááœááºááœá±ááŸááááºá áááºáá¡áááºážáá»á¬ážááᯠ./my-classes.ps1 ááá¯ááºááœáẠáááºááŸááºáá¬ážááẠááá¯ááŒáá«á áá¯á·
-
á¡áááºážáá»á¬ážááŸáá·áºá¡áá° ááá¯ááºáá áºáá¯ááᯠdotsource áá¯ááºááá¯ááºáááº-á ./my-classes.ps1á áááºážááẠáááºááááºááŸááááºáááºááœáẠmy-classes.ps1 ááᯠáá¯ááºáá±á¬ááºáááºááŒá áºááŒá®áž ááá¯áá±áá¬ááœáẠááá¯ááºá០á¡áááºážá¡á¬ážáá¯á¶ážááᯠáááºááŸááºáá±ážáááºááŒá áºáááºá
-
ááá·áºá áááºááŒáá¯áẠAPI áá»á¬áž (cmdlets) á¡á¬ážáá¯á¶ážááᯠáááºááá¯á·ááá·áº Powershell module áá áºáá¯ááᯠáááºáá®ážááá¯ááºááŒá®áž áá°áá®áá±á¬ááááºááŒáá·áº ááá·áº module manifest ááœáẠScriptsToProcess = "./my-classes.ps1" variable ááᯠáááºááŸááºááá¯ááºáááº- ./my-classes.ps1 ááœáẠáá¯ááºáá±á¬ááºáá«ááẠááá·áºáááºáááºážáá»ááºá
áááºááœá±ážáá»ááºááá·áº áááºááá·áºááœá±ážáá»ááºááŸá¯áááᯠPowershell á á¡áá»áá¯ážá¡á
á¬ážá
áá
áºááẠááá°áá®áá±á¬áá±áá¬áá»á¬ážá០áááºáá±á¬ááºáá¬áá±á¬ áá°áá®áá±á¬á¡áááºá¡áá»áá¯ážá¡á
á¬ážáá»á¬ážááᯠáááŒá±ááŸááºážááá¯ááºááŒá±á¬ááºáž áááááá«á
ááá°áá®áá±á¬áá±áá¬áá»á¬ážá០áá°áá®áá±á¬áá¯ááºááá¹áááá»á¬ážááŒáá·áº áá°áá®áá±á¬á¡áááºážááŸá
áºáá¯ááᯠáááºáááºáá¬ážáá±á¬áºáááºážá áááºááẠááŒá¿áá¬áá»á¬ážááŸáá·áº áááºááá¯ááºáááá¯ááºáá»á±ááŸááááºá
ááŸá±á·áááºáž
á¡áá»áá¯ážá¡á á¬ážááŒá±ááŸááºážááŸá¯ááŒá¿áá¬áá»á¬ážááᯠááŸá±á¬ááºááŸá¬ážááẠá¡áá±á¬ááºážáá¯á¶ážáááºážáááºážááŸá¬ áááºáá¡áááºážáá»á¬ážááᯠáá¯á¶ážá áœá²áá°áá»á¬ážá¡á¬áž áááºáá±á¬á¡áá«á០ááá±á¬áºááŒáááºááŒá áºáááºá á¡áá¯á¶ážááŒá¯áá°ááẠá¡áááºážáááºááŸááºáá¬ážáá±á¬ á¡áá»áá¯ážá¡á á¬ážááᯠáááºááœááºážááẠáá»áŸá±á¬áºááá·áºááá·áºá¡á á¬áž á¡áááºážááᯠááá¯ááºááá¯ááºáááºáá±á¬ááºááẠááá¯á¡ááºááŸá¯ááᯠáááºááŸá¬ážáá±ážááá·áº ááá·áº module á០áá¯ááºáá±á¬ááºáá»ááºáá áºáá¯ááᯠáá¯ááºáá°áá«á Cluster á¡ááœááºá áá»áœááºá¯ááºááá¯á·ááẠá¡áá¯á¶ážááŒá¯áá°ááŸáá·áº á¡áááºááŒá±áá±á¬ ááá·áºáááºáá±á¬ááºáá áºáá¯ááᯠáá¶á·ááá¯ážáá±ážááŒá®áž Cluster áá áºáá¯ááᯠááŒááºáá±ážááá·áº New-Cluster áá¯ááºáá±á¬ááºáá»ááºááᯠáá¯ááºáá°ááá¯ááºáá«áááºá
class Cluster {
[ValidatePattern("^[A-z]+$")]
[string] $Service
[ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
[string] $FlightingRing
[ValidateSet("EastUS", "WestUS", "NorthEurope")]
[string] $Region
[ValidateRange(0, 255)]
[int] $Index
}
function New-Cluster {
[OutputType([Cluster])]
Param(
[Parameter(Mandatory, ParameterSetName = "Id", Position = 0)]
[ValidateNotNullOrEmpty()]
[string] $Id,
[Parameter(Mandatory, ParameterSetName = "Components")]
[string] $Service,
[Parameter(Mandatory, ParameterSetName = "Components")]
[string] $FlightingRing,
[Parameter(Mandatory, ParameterSetName = "Components")]
[string] $Region,
[Parameter(Mandatory, ParameterSetName = "Components")]
[int] $Index
)
if ($Id) {
$Service, $FlightingRing, $Region, $Index = $Id -split "-"
}
[Cluster]@{
Service = $Service
FlightingRing = $FlightingRing
Region = $Region
Index = $Index
}
}
Export-ModuleMember New-Cluster
áá¬áááºá áá¬ááŸááá±ážáá²á
source: www.habr.com