A Case Study in Federal Corruption and Media Silence
Jun. 12th, 2025 09:50 pm![[syndicated profile]](https://www.dreamwidth.org/img/silk/identity/feed.png)
Alfred Koch 2025-06-11
За все успехи и провалы Украины отвечает только он, Зеленский, и больше - никто. Даже если он в них не виноват. Так несправедливо устроена жизнь. Не хочешь нести ответственность, ищешь виноватых? Отдавай суверенитет. Тогда и отвечать будет тот, кому ты его отдал.
Эту оборотную сторону суверенитета не каждый осознает. А между тем она есть. Ты глава свободного и независимого государства и никто тебе не указ? Прекрасно! Но тогда держи и вторую часть этого жребия: ты за все ответственен и не на кого спихнуть твои проблемы и неудачи.
Путин напал? Ты виноват. Запад не помогает? Ты виноват. Украинцы не хотят идти на фронт и бегут из армии? Ты виноват. Украина в дефолте и казна пуста? Ты виноват. И так далее…
Что? Ты так не хочешь и это несправедливо? Нет проблем: бери бумагу с ручкой и пиши заявление об отставке. Это не у тебя так все неказисто сложилось, это всегда так было. Шапка Мономаха реально тяжела. Это не выдумка. Свобода всегда имеет оборотной стороной ответственность. Как минимум - за себя. Ты хотел свободы для себя и для Украины - ты ее получил. Так что жаловаться некому: это был твой выбор.
12 июня состоялся выпуск 8.5 «Sigourney» консольного редактора текста GNU nano.
Список изменений:
--positionlog
.^O^Q
и ^X^Q
nano выходит со статусом ошибки.^L
просто центрирует курсор, а M-%
циклически его перемещает.--whitespace
принята, но не документирована.You can read the original post in its original format on Rtask website by ThinkR here: From lab to real life: How your Shiny application can survive its users
From prototype to production, make sure nothing breaks…
You’ve created a fantastic mockup and your client is delighted. You’re ready to move to production with your application. But one question haunts you: how can you ensure that your application will remain stable and functional through modifications and evolutions?
The answer comes down to one word: testing.
Three weeks ago, part of the ThinkR team was in Belgium to participate in the Rencontres R 2025. A conference around R whose objective is to offer the French-speaking community a place for exchange and sharing of ideas on the use of the R language across all disciplines.
On Tuesday afternoon, during a 15-minute presentation, I presented the use of testing in a Shiny application (presentation available in English and French). After several hours of preparation and a presentation, my mind was then test-oriented. At the end of the 3-day conference, I indulged in my second hobby (after R development): running. While I was running, a song played in my ears: Let’s Talk About Sex by Salt-N-Pepa. I could only hear: “Let’s talk about you and me […] Let’s talk about tests”. Professional bias? Surely.
Music at full volume, Let’s talk about tests!
Yo, I don’t think we should talk about this
Come on, why not ?
People might misunderstand what we’re tryin’ to say, you know ?
No, but that’s a part of life
[…]
Let’s talk about [tests], baby
Too often neglected in the R ecosystem, tests are nonetheless essential for guaranteeing the robustness and sustainability of your Shiny applications. In this article, we’ll explore a three-level testing strategy that will allow you to secure your code from development to production.
The development of a Shiny application often follows the same path: you start with a quick prototype, you iterate with your client (or yourself), you add features… and suddenly, you realize that your code has become complex and fragile.
Each new modification risks breaking an existing functionality. Each feature addition makes you fear introducing regressions. This is where tests become your best ally.
A well-thought-out testing strategy allows you to:
To effectively secure a Shiny application, we recommend a three-level approach:
Each level has its role and specificities. Together, they form a complete safety net for your application.
Unit tests consist of testing each function in isolation, independently of the rest of the application.
It’s like checking that a drawer opens correctly before installing it in a kitchen.
If you follow best practices and develop your Shiny application as a package (with {golem}
), unit tests integrate naturally into your workflow.
Let’s create our first application:
# install.packages("golem") golem::create_golem(path = "myShinyApp")
Once this code is executed, your package will open in a new session. You can immediately verify that your application launches correctly:
# Launch application in dev mode golem::run_dev()
In {golem}
, the development cycle is found in the /dev
folder. You’ll find all the functions you’ll need for development there.
The first file to follow is the dev/01_start.R
file. It contains all the functions you’ll need to start your project. Among them is the function: golem::use_recommended_tests()
. This function will create the test structure in your package, with tests recommended by {golem}
including one that verifies that your application will launch correctly. Convenient!
Let’s imagine we need a function in our application: calculate_average()
. We can execute: usethis::use_r("calculate_average")
to create the file that will contain our function.
# In R/calculate_average.R calculate_average <- function(values) { if (!is.numeric(values)) { stop("values must be numeric") } if (length(values) == 0) { return(0) } sum(values) / length(values) }
This function calculates an average. It has some “validators” to check the function inputs. To associate a unit test with it, we can execute: usethis::use_test(name = "calculate_average")
# In tests/testthat/test-calculate_average.R test_that("calculate_average works correctly", { # Test with numeric values expect_equal( object = calculate_average( values = c(10, 20, 30) ), expected = 20 ) # Test with empty vector expect_equal( object = calculate_average( values = 0 ), expected = 0 ) # Test with non-numeric input expect_error( object = calculate_average( values = c("a", "b") ), "values must be numeric" ) })
To verify that the calculate_average
function works as expected, we run the tests:
devtools::test()
If the tests are successful, we get:
You can modify/break a test to see and experiment with a failing test!
Replacecalculate_average(values = c(10, 20, 30))
withcalculate_average(values = c(10, 20, 1))
to see the result.
At this stage, unit tests allow us to test the business functions of our application. We don’t test the application per se, but we ensure good behavior of its business logic.
Integration tests verify that the different components of your application work correctly together. In Shiny, this means testing reactive flows, interactions between modules, and server logic.
We’ve checked if the drawer could open correctly. We’re now going to verify that it integrates correctly with the rest of the kitchen furniture: the other drawers, the worktop, etc…
Let’s modify our application a bit!
In the R/app_ui.R
file, replace the golem::golem_welcome_page()
function with the following code:
numericInput(inputId = "num1", label = "First value", value = 10), numericInput(inputId = "num2", label = "Second value", value = 10), numericInput(inputId = "num3", label = "Third value", value = 10), numericInput(inputId = "num4", label = "Fourth value", value = 10), actionButton(inputId = "go", label = "Calculate!"), textOutput(outputId = "result")
This should give:
app_ui <- function(request) { tagList( golem_add_external_resources(), fluidPage( numericInput(inputId = "num1", label = "First value", value = 10), numericInput(inputId = "num2", label = "Second value", value = 10), numericInput(inputId = "num3", label = "Third value", value = 10), numericInput(inputId = "num4", label = "Fourth value", value = 10), actionButton(inputId = "go", label = "Calculate!"), textOutput(outputId = "result") ) ) }
We’ve just added 5 inputs including 4 inputs to enter a numeric value and an action button. Finally, a textOutput
will allow us to display text.
Be careful to put a comma between the different elements of your UI.
To verify that this code works, we can launch the application:
# Launch application in dev mode golem::run_dev()
It’s time to connect the UI part of our application with the server part. For this, in R/app_server.R
:
app_server <- function(input, output, session) { rv <- reactiveValues() observeEvent(input$go, { rv$avg <- calculate_average( c(input$num1, input$num2, input$num3, input$num4) ) showNotification( "Calculation completed!", duration = 3 ) }) output$result <- renderText({ req(rv$avg) paste("Average:", rv$avg) }) }
This code just created a reactiveValues()
, a box that will store the different results in our application. When the button is clicked, the code inside the observeEvent
will be executed. We store the result of our calculate_average
function in the reactiveValues
.
# Launch application in dev mode golem::run_dev()
Shiny provides the testServer()
function which allows testing server logic without launching the user interface.
In a new file usethis::use_test(name = "server")
, copy the following code:
testServer(app_server, { session$setInputs(num1 = 5) session$setInputs(num2 = 5) session$setInputs(num3 = 5) session$setInputs(num4 = 5) session$setInputs(go = 1) expect_equal( object = rv$avg, expected = 5 ) session$setInputs(num1 = 10) session$setInputs(num2 = 20) session$setInputs(num3 = 30) session$setInputs(num4 = 12) session$setInputs(go = 2) expect_equal( object = rv$avg, expected = 18 ) })
This test simulates values for the different inputs in the server and also simulates a click on the button session$setInputs(go = 1)
.
We then expect the result stored in the reactiveValues
to be 5:
expect_equal( object = rv$avg, expected = 5 )
As before, to run the tests in our application, we execute:
devtools::test()
You can always break a test to experiment with a failing test!
Replacesession$setInputs(num3 = 30)
withsession$setInputs(num3 = 2)
to see the result.
This integration testing logic with testServer
can also be used with modules in Shiny! Complete and more complex applications can therefore be tested.
We’ve just tested the nested interactions in the application and particularly the different interactions. However, this remains very “programmatic” and doesn’t reflect the real experience of a user in a browser.
End-to-End tests simulate a real user interacting with your application in a real browser. This is the testing level closest to the final user experience.
The objectives are multiple here:
We’ve checked the drawers individually. We’ve also verified that they could all be assembled together. Now, it’s time to test cooking a real meal in the kitchen!
{pw}
packageFor E2E (End-to-End) tests in the R ecosystem, we recommend using Playwright via the {pw}
package developed by ThinkR.
Installation and configuration:
# Package installation (in development) devtools::install_github("ThinkR-open/pw") # Initialize test structure pw::pw_init()
This command creates the following structure:
tests/ ├── playwright/ ├── tests/ ├── default.test.ts └── testthat/ ├── test-calculate_average.R ├── test-golem-recommended.R ├── test-server.R └── test-playwright.R
E2E tests should be placed in the playwright/tests/
folder. A test is already available, it contains:
import { test, expect } from '@playwright/test'; test('has body', async ({ page }) => { await page.goto('http://127.0.0.1:3000'); await expect(page.locator('body')).toBeVisible(); });
This code will launch the application in a browser and verify the presence of our body
. We can already run this test:
pw::pw_test()
Playwright also provides a report that we can consult:
pw::pw_show_report()
Playwright runs this test in 3 different browsers: chromium
, firefox
and webkit
.
OK, but how do we test our application?
Good news, you don’t necessarily need to learn TypeScript
to produce E2E tests with Playwright:
pw::pw_codegen()
This function will open a browser, in which we’ll be able to simulate clicks and actions, as a user.
Playwright will record our actions and store them in a new file in tests/playwright/tests
:
import { test, expect } from '@playwright/test'; test('test', async ({ page }) => { await page.goto('http://localhost:3000/'); await page.getByRole('spinbutton', { name: 'First value' }).click(); await page.getByRole('spinbutton', { name: 'First value' }).fill('20'); await page.getByText('First value Second').click(); await page.getByRole('button', { name: 'Calculate!' }).click(); await page.getByText('Average:').click(); });
This test is far from perfect and we’ll return in a future article to the syntax of tests with Playwright and how to optimize them. E2E tests also work in CI and we’ll also return to this in a future article.
Meanwhile, our application here is tested in a context that comes closest to the reality of our future users. We’ve just secured the proper functioning of the application, both on business logic and on the UI part.
We can run all the tests in our application:
devtools::test()
A commonly accepted rule in the industry is the test pyramid:
Unit tests:
Integration tests:
End-to-End tests:
Implementing a three-level testing strategy radically transforms how you develop and maintain your Shiny applications.
Concrete benefits:
To start today:
{golem}
testServer()
{pw}
for your main user journeysDon’t hesitate to contact us if you want to deepen the implementation of tests in your Shiny applications!
You can also find Colin Fay who will lead a workshop on Wednesday, October 8, 2025 during the Shiny In Production conference organized by Jumping Rivers.
You can also find all our training courses on Shiny application development here!
This post is better presented on its original ThinkR website here: From lab to real life: How your Shiny application can survive its users
Кстати, раз уж полез в интернет интересоваться подобной чепухой, то и посмотрел среднюю пенсию в Израиле. Написано, что 6000 шекелей. Это, если опять же по офицальному курсу, получается 133 320 рублей.
Совсем уже какая-то лживая западная пропаганда, если в пересчете на картошку.
К своему изумлению наткнулся на Евроньюс на огромный репортаж про то, что картофель в России подорожал по сравнению с прошлым годом в три раза и сейчас стоит практически сто двадцать рублей за килограмм.
Я, к стыду своему, представления не имею, дорого ли это. Несколько отстранился от такого рода проблем, хотя, честно говоря, никогда особо и не был в них специалистом.
Но услышав столь эмоциональную информацию от европейских журналистов, подкрепленную высказываниями россиян, которые на экране голосили про: "Ужас, ужас", я спросил у жену, а сколько эта самая картошка стоит в Израиле?
Супруга посмотрела не меня как на идиота и ответила, что, естественно, представления не имеет, она не смотрит на ценник, когда покупает картошку. И добавила с подозрением, мол, а что, у меня настолько серьезные финансовые проблемы на старости лет возникли? Пора готовиться к худшему?
Я как мог её успокоил и полез в интернет. Там оказалось написано, что средняя цена килограмма картофеля в стране 1,3 шекеля. То есть, если по официальному курсу примерно рублей тридцать.
Что, реально в России сейчас картошка в четыре раза дороже чем в Израиле? Не может быть. Хуйня какая-то. Явная вражеская клевета и пропаганда.
Сегодня — День России, говорят.
А один мой друг в этой связи заметил: «Если раньше пелось: «Жила бы страна родная, и нету других забот», то теперь - «Сдохни, тварь, и пусть здесь всё горит огнём».
Но он, конечно, несколько преувеличивает.
Играет в юношеский максимализм, подражая нашим детишкам с их бескомпромиссностью.
Нет, страна — весьма даже симпатичная.
Берёзки в лесочках, окушки в речках.
Люди?
Совсем оскотинились, вовсе утратили всякое нравственное чувство, когда поддерживают мерзавцев во власти и их поганые затеи?
Ну, если не изобретать себе некоего «сферического человека в вакууме», а рассматривать людей такими, каковы они есть, то — они, ан масс, примерно одинаковы во все времена и во всех нациях.
И могут, конечно, немного влиять некие идеалистические представления, которые людям внушают с детства в той или иной культуре, но на самом деле — не так уж сильно.
Ещё того меньше — влияет т. н. «пропаганда», довольно беспомощная штука, чьё могущество, разумеется, склонны дико раздувать торговцы балабольством всех «конфессий».
Но что реально влияет на возможность нравственного выбора — так это разнообразие источников финансирования.
Когда так выходит, что источник остаётся один — очень нетрудно оказывается убедить людей в том, что именно ему и следует служить, ведь именно из него они могут кормить своих детей (а значит, именно этот источник — и есть «нравственность»).
Впрочем, они могут даже и не считать его «нравственным», могут и не любить его, а всё равно служить ему — как, скажем, какие-нибудь голландцы или чехи исправно работали в войну на нацистов, вовсе не имея к ним никаких симпатий.
Поэтому, считайте меня «географическим детерминистом», но, как я не раз говорил, главной проблемой России — видится её «проклятие сверхконтинентальности». Чисто географическое устройство, которое делает предельно лёгкими затеями концентрацию власти и контроль над всеми товарными потоками, а соответственно — крайне затрудняет возникновение действенной оппозиции, когда все аспекты благополучия населения завязаны на властный центр и независимых источников доходов практически нету.
Однако, времена меняются.
И сейчас, как бы ни трепыхался этот деспотический центр, альтернативные товарные потоки и альтернативные источники доходов — всё же появляются, и значение их нарастает.
Ещё же больше изменится ситуация, когда береговая линия Севледока сделается пригодной для круглогодичной контрабанды.
Ведь как сказал мудрец, «Контрабанда — мать демократии, а коррупция — её повивальная бабка».
И по мере развития неподконтрольных государству товарно-денежных потоков — лояльность населения и его холуйское низкопоклонство перед деспотизмом будет таять, как утренний дым над водой.
Ибо в действительности — да прекрасно знают русские люди, что за дрянь их государство, со времём Иоанна «Бунши» и до наших дней.
И все, от простых работяг до обер-прокуроров, в приватных беседах, конечно, признают, что это именно дрянь несусветная, эта Московия, но — куда от неё деваться-то и что возможно взамен на этих землях?
Тут ведь особо обескураживающей выглядит попытка переменить суть Московии, осуществлённая в начале двадцатого века людьми, которые искренне верили в «царство свободы».
Получился же — вовсе такой мрак, что потом с ностальгией поминали «хруст французских булок» в той царской России, «которую мы потеряли» (и стараясь забыть про «хруст народных косточек под неумолимым ярмом деспотизма»).
Да, уроки Революции — конечно, обескураживают желающих «всё тут изменить».
Но тем не менее, мы-то считаем, что если предпосылки для московитского деспотизма имеют климатический, географический и логистический характер — то, значит, именно его и надо изменять, чтобы освободить Россию от Московии.
А заодно — всемерно использовать вновь нарождающиеся технологии для выведения экономических потоков из-под контроля государства.
И мы настроены довольно оптимистически в этом плане.
Да, безусловно Путинская Россия — это одно из самых мерзких государств, когда-либо поганивших собою этот глобус, но именно по этой причине именно здесь — мы видим наивысшие шансы для скорейшего отказа от сколько-нибудь сильной национально-территориальной государственности и перехода к новым, более гибким и более адекватным формам организации власти.
Поэтому — почему бы и не отпраздновать День России?
Разумеется — никто в здравом уме не пожелает «долголетия» тому российскому государству, что есть сейчас, но окушки в речках — уже скоро смогут вдыхать воздух свободы всеми жабрами.
Что, не всяким жабрам нужен воздух свободы?
Ну, значит, придётся эволюционировать.