• Как оганизовать функционал, при добавлении комментария под книгой сообщение о добавлении комментария должно всплывать всем подключённым пользователям?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Необходимо добавить метод для отправки сообщения всем подключенным клиентам при добавлении нового комментария.

    public void sendCommentToAllClients(Comment comment) {
        if (ns != null) {
            ns.broadcast("comment:save", comment);
        }
    }


    После сохранения комментария, необходимо отправить его через SocketEmitter всем клиентам.

    @PostMapping("/add-comment")
    public ResponseEntity<Comment> addComment(@RequestBody BookResponse reqBody, Authentication authentication) {
        try {
            // (...)
            commentRepository.save(comment);
    
            // Отправляем комментарий всем подключенным клиентам
            socketEmitter.sendCommentToAllClients(comment);
    
            return ResponseEntity.status(HttpStatus.OK).body(comment);
        } catch (Exception ex) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .build();
        }
    }


    Вы уже реализовали прослушивание события comment:save на клиенте, поэтому необходимо только убедиться, что данные обрабатываются корректно.

    useEffect(() => {
        socket.on('comment:save', (data: Comment) => {
            const comment = JSON.parse(data as any)
            console.log('comment', comment);
            props.book.comments.push(comment);
            // Если setBookInState обновляет состояние, это может быть полезно для перерисовки
            // props.setBookInState({...props.book}); 
            toast.info('Got event from socket: Book ' + comment.text)
        });
    }, [socket]);
    Ответ написан
  • How docker compose push?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    1.Использование одного образа для разных сервисов: В вашем файле docker-compose.yml все три сервиса (app, webserver и db) используют один и тот же образ cubinez85/docker_compose/web:latest. Это неправильно, так как каждый из этих сервисов должен иметь свой образ, настроенный для конкретной задачи.

    2.Конфигурация NGINX: Если ваш NGINX не настроен правильно, он не сможет перенаправлять запросы на PHP контейнер. Вам нужно удостовериться, что конфигурация NGINX правильно перенаправляет запросы на fastcgi_pass адрес вашего PHP контейнера.

    3.Настройка PHP-FPM: Убедитесь, что PHP-FPM в вашем app контейнере настроен для прослушивания соединений (обычно это делается с помощью listen = 9000 в файле www.conf).

    Измените docker-compose.yml:
    version: '3.7'
    services:
        app:
            build: ${PWD}/fpm/
            image: cubinez85/docker_compose/app:latest
            container_name: app
            volumes:
                - ${PWD}/code:/data
            networks:
                - app-network
    
        webserver:
            build: ${PWD}/nginx/
            image: cubinez85/docker_compose/nginx:latest
            container_name: webserver
            volumes:
                - ${PWD}/code:/data
            ports:
                - "80:80"
                - "443:443"
            networks:
                - app-network
    
        db:
            image: mysql:latest
            container_name: db
            ports:
                - "3306:3306"
            environment:
                MYSQL_DATABASE: ${MYSQL_DATABASE}
                MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
            volumes:
                - ${PWD}/dbdata:/var/lib/mysql
            networks:
                - app-network
    
    networks:
        app-network:
            driver: bridge


    Убедитесь что в nginx.conf есть что-то вроде:
    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        ...
    }

    Здесь app:9000 указывает на ваш PHP контейнер и порт 9000, который слушает PHP-FPM.
    Ответ написан
    7 комментариев
  • Как реализовать роутинг между элементами внутри модального окна?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    1. Роутинг внутри модалки: Можно использовать библиотеку react-router-dom даже внутри модального окна для обеспечения роутинга между различными экранами.

    Создайте отдельный <Switch> внутри модалки:
    <Modal>
        <Switch>
            <Route path="/modal/presets" component={Presets} />
            <Route path="/modal/edit/:id" component={EditPhrase} />
            <Route path="/modal/create" component={CreatePhrase} />
            <Route path="/modal/delete/:id" component={DeletePhrase} />
            {/* ... */}
        </Switch>
    </Modal>


    2. Компоненты экранов: Разделите разные экраны модалки на отдельные компоненты. Это позволит упростить их управление и сделать код более читаемым.

    3. Состояние: Вместо того чтобы использовать сложную систему контекстов, можно просто использовать локальное состояние (useState) или глобальное состояние (например, с помощью redux или mobx), в зависимости от ваших потребностей.

    4. Хуки: Избегайте избыточного использования useMemo, useCallback и прочих, если нет явных причин для оптимизации. Часто избыточное использование этих хуков только усложняет код и на самом деле может замедлить приложение.

    Псевдокод:

    Основной компонент модалки:
    function ModalComponent() {
        return (
            <Modal>
                <Switch>
                    <Route path="/modal/presets" component={Presets} />
                    <Route path="/modal/edit/:id" component={EditPhrase} />
                    <Route path="/modal/create" component={CreatePhrase} />
                    <Route path="/modal/delete/:id" component={DeletePhrase} />
                    {/* ... */}
                </Switch>
            </Modal>
        );
    }


    Компонент пресетов:
    function Presets() {
        const phrases = ... // получите фразы
    
        return (
            <div>
                {phrases.map(phrase => (
                    <div key={phrase.id}>
                        {phrase.text}
                        <Link to={`/modal/edit/${phrase.id}`}>Редактировать</Link>
                    </div>
                ))}
                <Link to="/modal/create">Создать новую фразу</Link>
            </div>
        );
    }


    Этот пример очень общий, но я надеюсь, что он даст вам представление о том, как можно структурировать ваш код. Если что-то кажется запутанным или избыточным, вероятно, так оно и есть
    Ответ написан
    Комментировать
  • Почему @ModelAttribute возвращает значение только одной колонки?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Когда вы используете атрибут disabled для<input>, значение этого поля не отправляется на сервер вместе с другими данными формы. Таким образом, при отправке формы, поля с атрибутом disabled будут игнорироваться.

    В вашем коде, поле *{settings[__${item.index}__].name} установлено как disabled, и поэтому name всегда будет null при обработке POST-запроса на сервере.

    Чтобы решить эту проблему, вы можете использовать дополнительное скрытое поле для передачи значений, которые не предназначены для редактирования пользователем, например

    <td>
        <input disabled th:value="*{settings[__${item.index}__].name}">
        <input type="hidden" th:field="*{settings[__${item.index}__].name}">
    </td>
    Ответ написан
  • Как посчитать звёзды на небе?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Здесь можно посмотреть решение, выполненное на Python. Здесь репозиторий проекта. Чисто в практических целях можете реализовать то же с Java, никакая нейросеть вам для этого не нужна. Желаю удачи.
    Ответ написан
    1 комментарий
  • Как проверить список на введенное слово из input и вывести количество сходств?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Scanner input = new Scanner(System.in);
            String[] list = new String[4];
            System.out.println("Insert list elements: ");
            for (int i = 0; i < list.length; i++) {
                list[i] = input.nextLine();
            }
            System.out.print("Inserted list elements: ");
            for (String s : list) {
                System.out.print(" " + s);
            }
            System.out.print("Ввод нужного адреса: ");
            String address = input.nextLine();
            long count = IntStream.range(0, list.length).filter(i -> address.equals(list[i])).count();
            System.out.println(count);
    Ответ написан
    2 комментария
  • В чем разница между str и &str?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    str представляет собой неизменяемую последовательность байтов UTF-8 динамической длины где-то в памяти. Поскольку размер неизвестен, его можно обрабатывать только за указателем. Это означает, что str чаще всего используется как &str — ссылка на некоторые данные UTF-8, обычно называемые «срезом строки» или просто «срезом». Срез — это просто представление данных, и эти данные могут быть где угодно, например:

    - В статическом хранилище: строковый литерал "foo"представляет собой файл &'static str. Данные жестко закодированы в исполняемый файл и загружаются в память при запуске программы.

    - Внутри выделенной кучи: String разыменовывает представление &str в строке.

    - На стеке: Например, в следующем примере создается массив байтов, выделенный стеком, а затем он получает представление этих данных как &str:

    use std::str;
    
    let x: &[u8] = &[b'a', b'b', b'c'];
    let stack_str: &str = str::from_utf8(x).unwrap();


    Используйте String, если вам требуется владение строковыми данными (например, передача строк в другие потоки или их создание во время выполнения), и используйте &str, если вам нужно только представление строки.
    Ответ написан
    1 комментарий
  • Как распарсить текстовый документ на java?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Вы можете использовать регулярные выражения для поиска нужной вам информации. Например, для поиска чисел в тесте вы можете использовать следующее решение:

    Pattern p = Pattern.compile("[0-9]+");
    Matcher m = p.matcher("string1234(((more56))7))string890");
    while (m.find()) {
        System.out.println(m.group()); //1234 56 7 890
    }
    Ответ написан
  • Как обойти ошибку 0x0000011b?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Создай новый локальный порт при добавлении принтера и в пути укажи путь \\HOST_NAME\PRINTER_NAME
    6231fad39648b705635588.png
    Ответ написан
  • Как откатиться назад на стабильный commit и при этом сохранить полезный код, который ты сделал после допущенной ошибки?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Временно переключиться на другой коммит:

    Вы можете временно переключиться на другой коммит:

    git checkout <your_commit_sha>

    Если вы хотите делать коммиты, пока вы временно на другой ветке:

    git checkout -b old-state <your_commit_sha>

    Чтобы вернуться туда, где вы были, просто снова checkout ветку, в которой вы были. (Если вы внесли изменения, при переключении веток, вам придется обращаться с ними соответствующим образом.

    Отменить раннее опубликованные коммиты новыми коммитами:

    Если вы опубликовали изменения, вы, вероятно, не захотите сбрасывать ветку, так как это фактически перепишет историю коммитов. Создайте коммит с обратным патчем, чтобы отменить его. Таким образом, вы не переписываете историю коммитов. Вы можете поступить следующим образом:

    # Это создаст три отдельных коммита возврата:
    git revert <your_commit_sha1> < your_commit_sha2> <your_commit_sha3>
    # Это вернет последние два коммита. Также принимает диапазоны:
    git revert HEAD~2..HEAD
    # Точно так же вы можете отменить ряд коммитов, используя хэши коммитов (не включая первый хеш):
    git revert <your_commite_sha>
    # Отмена мердж коммита:
    git revert -m 1 <merge_commit_sha>
    # Чтобы получить только один коммит, вы можете использовать 'rebase -i', 
    # Или вы можете сделать это вручную (обязательно сделайте это на верхнем уровне вашего репозитория)
    # Привести ваш индекс и дерево в нужное состояние, не меняя HEAD:
    git checkout <your_target_commit_sha>
    # После чего обязательно зафиксируйте коммит. Будьте уверены в том, что вы сделали на 150% и напишите хорошее сообщение с описанием того, что вы только что сделали:
    git commit


    Раздел git-scm.com, где описывается использование git-revert. Если вы решите, что все-таки не хотите возвращаться, вы можете отменить возврат (как описано здесь) или вернуться к состоянию до возврата. В этом случае вам также может быть полезен этот ответ:
    тык
    Ответ написан
    Комментировать
  • Как усовершенствовать код, чтобы можно было работать со знаками препиания?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Вы можете использовать просмотр вперед и просмотр назад, которые являются функциями регулярных выражений.
    Вы даже можете поместить плейсхолдеры, например %1$s и использовать String.format для замены плейсхолдеров на фактическую строку, которую вам нужно использовать.

    Простое решение для Java 8 и новее:

    public class Main {
        public static final String DELIMITER = "((?<=%1$s)|(?=%1$s))";
    
        public static void main(String[] args) {
            String task = "Заменить, в тексте все! слова длиной? не больше 6 символов; в обратном порядке начинающиеся на: согласную букву ";
            String[] arr = task.split(String.format(DELIMITER, "[:?;!. ]"));
            String result = Arrays.stream(arr)
                    .map(s -> !check(s.charAt(0)) && !length(s) ? reverse(s) : s)
                    .collect(Collectors.joining());
            System.out.println(result); /* Заменить, в етскет есв! аволс йонилд? ен ешьлоб 6 символов; в обратном порядке начинающиеся ан: согласную увкуб */
    
        }
    
        public static boolean check(char c) {
            return "аиеёоуыэюя".indexOf(c) > -1;
        }
    
        public static boolean length(String s) {
            return s.length() > 6;
        }
    
        public static String reverse(String s) {
            return new StringBuilder(s).reverse().toString();
        }
    }
    Ответ написан
    Комментировать
  • Как удалить из hashmap все элементы с одинаковым значением?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Использовать HashSet, если использование HashMap не обязательное условие задачи.
    Ответ написан
    2 комментария
  • URL на личный кабинет пользователя?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Spring Security предоставляет класс SecurityContextHolder, который позволяет выполнять поиск текущего аутентифицированного пользователя через:
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();

    Authentication предоставляет следующие методы:

    - Получить имя пользователя вошедшего в систему: getPrincipal()
    - Получить пароль аутентифицированного пользователя: getCredentials()
    - Получить назначенные роли аутентифицированного пользователя: getAuthorities()
    - Получить дополнительную информацию об аутентифицированном пользователе: getDetails()

    Вы можете добавить к Model текущему авторизованному пользователю в вашем контроллере:

    public String foo(Model model) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String username = user.getUsername();
        model.addAttribute("username", username);
        return "your_view";
    }

    а затем в вашем html:
    <td th:each="user : ${user}">
        <a th:if="${username == user.username}" th:href="@{/profile/{id}(id=${user.id}}">Profile</a>
    </td>
    Ответ написан
    Комментировать
  • Как узнать какие версии библиотек установлены в Java проекте?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Библиотеки могут быть добавлены в проект как в виде java-архива вручную, так и управляться системой автоматической сборки проектов по типу maven/gradle/ant. Делают абсолютно то же самое, а именно добавляют java-архивы в раздел "External Libraries" вашего проекта, в названии самого архива как правило указывается версия этой библиотеки, либо если вы пользуетесь системой авто-сборки, то версия библиотеки указывается в разметке при ее добавлении.
    Ответ написан
    Комментировать
  • Как правильней создать структуру игровых серверов?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Рекомендую к прочтению. Статья довольно противоречивая и после прочтения возникают некоторые вопросы, но в целом неплохо описывает архитектуру многих ММО на примере java - эмулятора Lineage 2. Транслировать на ваши реалии проблем не составит, я думаю.
    https://habr.com/ru/post/550424/
    Ответ написан
    Комментировать
  • Какой из бесплатных курсов посоветуете пройти?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Согласен с Dmitry Roo в плане литературы. Насчет Javarush не уверен. Да, там все на русском. Да, концепт прикольный, без нудятины + дополнительный материал в виде статей от пользователей. Но проект позиционирует себя как обучающий портал с большим количеством практических задач, которые как по мне расчитаны на механическое заучивание основ, но никак не на понимание того, как оно работает. Во многих задачах, особенно тех, где требуется написать клон какой-либо старой игры за тебя дописывают часть кода, что как по мне не очень хорошо, если речь идет об обучении. Пусть код будет плохой или вообще неправильный, но ты потратишь время и найдешь в чем была ошибка, тем самым поняв, почему она вообще возникла. Hyperskill от Jetbrains в этом плане гораздо лучше + 2 месяца бесплатного обучения с доступом к любым проектам и разделам. Но опять же все это сугубо индивидуально. Лучший способ научиться чему-то - поставить себе задачу и копать материал для решения этой задачи.
    Ответ написан
    Комментировать
  • Как поменять элементы массива?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Простое решение с использованием Stream API:
    public static void main(String[] args) {
            int[] array = {1, 2, 3, 4, 5};
            int[] result = IntStream.rangeClosed(1, array.length)
                    .map(i -> array[array.length - i])
                    .toArray();
            System.out.println(Arrays.toString(result));
        }


    Еще проще с использованием класса Collections, интерфейса List и обертки над примитивом int:
    public static void main(String[] args) {
            Integer[] array = {1, 2, 3, 4, 5};
            List<Integer> list = Arrays.asList(array);
            Collections.reverse(list);
            System.out.println(Arrays.toString(list.toArray()));
        }


    Если не нравятся предыдущие варианты, то можно по классике:
    public static void main(String[] args) {
            int[] array = {1, 2, 3, 4, 5};
            for (int i = 0; i < array.length / 2; i++) {
                int tmp = array[i];
                array[i] = array[array.length - 1 - i];
                array[array.length - 1 - i] = tmp;
            }
        }
    Ответ написан
    Комментировать
  • Как работает рекурсия в Java?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Когда вы вызываете метод из другого метода, то вызывающий метод приостанавливается в частично завершенном состоянии. Все значения переменных вызывающей функции физически сохраняются в памяти. Метод, вызванный вызывающим его методом размещается поверх вызывающего метода и происходит все то же самое, пока соблюдается рекурсивный случай (условие, при котором метод вызывает сам себя). Когда происходит базовый случай (условие, при котором рекурсия должна быть остановлена), методы по очереди возвращают управление вызвавшему их методу, вплоть до полного завершения исходного (самого нижнего) метода.
    Ответ написан
    Комментировать
  • Зачем json считывать через Scanner?

    Erik_Mironov
    @Erik_Mironov
    Старые вопросы: *Dies from cringe*
    Зачем считывать файл через Scanner?


    С помощью класса Scanner вы можете читать данные из файла точно так же, как и с помощью BufferedReader'а, BufferedInputStream'а и других классов, способных читать файловые данные. Почему же здесь использовали Scanner известно только тому, кто написал этот класс, потому что есть более подходящие классы для подобных операций.

    Зачем использовать цикл? нельзя взять и всю прочитать без цикла?


    Поскольку это универесальный класс и в реальной рабочей ситуации вы бы не знали, файлы какого размера будут читать с помощью этого метода, гораздо лучше использовать циклы и читать файл построчно. Вы можете прочитать файл целиком, но на больших файлах это будет работать ужасно медленно.

    Зачем строке происваевать данные файла?


    В переменной fromJson мы сохраняем прочитанные данные, а поскольку JSON представляет собой обычный набор текста, то использование типа String более чем подходит для этой операции. Вы также можете использовать коллекции или массивы для этого.

    Зачем указывать тип объекта в конце?


    Метод fromJson десериализует JSON, прочитанный из Reader'а (в данном случае из строки) в объект класса, указанного вторым аргументом.
    Ссылочка на спек класса
    https://www.javadoc.io/doc/com.google.code.gson/gs...
    Ответ написан
    Комментировать